Quantcast
Channel: Node.jsタグが付けられた新着記事 - Qiita
Viewing all articles
Browse latest Browse all 8862

【第2回】「みんなのポートフォリオまとめサイト」を作ります~REST API編~ 【Cover】成果物URL: https://minna.itsumen.com

$
0
0

ワイ 「この記事のカバーです」
https://qiita.com/kiwatchi1991/items/58b53c5b4ddf8d6a7053

バックナンバー

【第1回】「みんなのポートフォリオまとめサイト」を作ります~プロトタイプ作成編~

成果物

https://minna.itsumen.com

リポジトリ

フロントエンド

https://github.com/yuzuru2/minna_frontend

バックエンド

https://github.com/yuzuru2/minna_backend

コレクション定義(テーブル定義)

ワイ 「今回はNoSQLMongoDBを使ってます」
ワイ 「コレクションとはRDBでいうテーブル的なやつです」

RDBMongoDB
スキーマデータベース
テーブルコレクション
カラムフィールド
レコードドキュメント

①Users(ユーザ)

uid: unique
物理名論理名
uidユーザIDstring
name名前string
twitterUrlTwitterのURLstring
githubUrlGitHubのURLstring
createdAt作成時間Date
updatedAt更新時間Date
src/mongoose/collection/users.ts
import*asmongoosefrom'mongoose';importSchemafrom'src/mongoose';constmodel_name='users';interface_interface{uid:string;name:string;twitterUrl:string;githubUrl:string;createdAt:Date;updatedAt:Date;}interfacei_modelextendsmongoose.Document{}interfacei_modelextends_interface{}constmodel=mongoose.model(model_name,newSchema({uid:{type:String},name:{type:String,minlength:1,maxlength:15},twitterUrl:{type:String},githubUrl:{type:String},createdAt:{type:Date},updatedAt:{type:Date},}).index({uid:1},{unique:true}));// 作成exportconstcreate=async(params:Pick<i_model,'uid'>)=>{const_data:_interface={uid:params.uid,name:'名無し',twitterUrl:'',githubUrl:'',createdAt:newDate(),updatedAt:newDate(),};return(awaitmodel.insertMany([_data]))asi_model[];};// 抽出exportconstfind=async(params:Pick<i_model,'uid'>)=>{const_data:Pick<i_model,'uid'>={uid:params.uid};return(awaitmodel.find(_data))asi_model[];};// 更新exportconstupdate=async(uid:string,params:Pick<i_model,'name'|'twitterUrl'|'githubUrl'>)=>{returnawaitmodel.updateOne({uid:uid},{$set:{...params,updatedAt:newDate()}});};

②Products(ポートフォリオ)

_id: unique
物理名論理名
_idポートフォリオのIDstring
uidユーザIDstring
typeポートフォリオのタイプnumber
titleポートフォリオのタイトルstring
urlポートフォリオのURLstring
repoリポジトリのURLstring
createdAt作成時間Date
updatedAt更新時間Date
src/mongoose/collection/products.ts
import*asmongoosefrom'mongoose';importSchemafrom'src/mongoose';constmodel_name='products';constpagingNum=5;interface_interface{uid:string;type:number;title:string;url:string;repo:string;createdAt:Date;updatedAt:Date;}interfacei_modelextendsmongoose.Document{}interfacei_modelextends_interface{}constmodel=mongoose.model(model_name,newSchema({uid:{type:String},type:{type:Number,min:0,max:5},title:{type:String,minlength:1,maxlength:30},url:{type:String,minlength:1,maxlength:100},repo:{type:String,maxlength:100},createdAt:{type:Date},updatedAt:{type:Date},}));// 作成exportconstcreate=async(params:Pick<i_model,'uid'|'type'|'title'|'url'|'repo'>)=>{const_data:_interface={uid:params.uid,type:params.type,title:params.title,url:params.url,repo:params.repo,createdAt:newDate(),updatedAt:newDate(),};return(awaitmodel.insertMany([_data]))asi_model[];};// 更新exportconstupdate=async(id:string,uid:string,params:Pick<i_model,'type'|'title'|'url'|'repo'>)=>{returnawaitmodel.updateOne({_id:id,uid:uid},{$set:{...params,updatedAt:newDate()}});};// 削除exportconstdeleteProduct=async(id:string,uid:string)=>{returnawaitmodel.deleteOne({_id:id,uid:uid});};// 全投稿数exportconstcountAll=async()=>{returnmodel.find({}).countDocuments();};// ジャンル別投稿数exportconstcountType=async(type:number)=>{returnmodel.find({type:type}).countDocuments();};// タイトル別投稿数exportconstcountTitle=async(title:string)=>{returnmodel.find({title:{$regex:title}}).countDocuments();};// ユーザ別投稿数exportconstcountUser=async(uid:string)=>{returnmodel.find({uid:uid}).countDocuments();};// ページング全投稿exportconstpagingAll=async(num:number)=>{returnawaitmodel.aggregate([{$match:{},},{$lookup:{from:'users',localField:'uid',foreignField:'uid',as:'users_info',},},{$sort:{createdAt:-1},},{$skip:num*pagingNum,},{$limit:pagingNum},{$project:{_id:'$_id',type:'$type',title:'$title',url:'$url',repo:'$repo',name:'$users_info.name',uid:'$uid',createdAt:'$createdAt',updatedAt:'$updatedAt',},},]);};// ページングタイプ別exportconstpagingType=async(num:number,type:number)=>{returnawaitmodel.aggregate([{$match:{type:type,},},{$lookup:{from:'users',localField:'uid',foreignField:'uid',as:'users_info',},},{$sort:{createdAt:-1},},{$skip:num*pagingNum,},{$limit:pagingNum},{$project:{_id:'$_id',type:'$type',title:'$title',url:'$url',repo:'$repo',name:'$users_info.name',uid:'$uid',createdAt:'$createdAt',updatedAt:'$updatedAt',},},]);};// ページングタイトル別exportconstpagingTitle=async(num:number,title:string)=>{returnawaitmodel.aggregate([{$match:{title:{$regex:title},},},{$lookup:{from:'users',localField:'uid',foreignField:'uid',as:'users_info',},},{$sort:{createdAt:-1},},{$skip:num*pagingNum,},{$limit:pagingNum},{$project:{_id:'$_id',type:'$type',title:'$title',url:'$url',repo:'$repo',name:'$users_info.name',uid:'$uid',createdAt:'$createdAt',updatedAt:'$updatedAt',},},]);};// ページングユーザ別exportconstpagingUser=async(num:number,uid:string)=>{returnawaitmodel.aggregate([{$match:{uid:uid,},},{$lookup:{from:'users',localField:'uid',foreignField:'uid',as:'users_info',},},{$sort:{createdAt:-1},},{$skip:num*pagingNum,},{$limit:pagingNum},{$project:{_id:'$_id',type:'$type',title:'$title',url:'$url',repo:'$repo',name:'$users_info.name',uid:'$uid',createdAt:'$createdAt',updatedAt:'$updatedAt',},},]);};

REST API


ユーザ作成・ログイン

リクエストURL

Post
/v1/create/user

リクエストヘッダー

Authorization: Firebase Authorizationで発行されたjwttoken
Content-Type: application/json

リクエストパラメーター

{}

レスポンス

{}


ポートフォリオ投稿

リクエストURL

Post
/v1/create/product

リクエストヘッダー

Authorization: Firebase Authorizationで発行されたjwttoken
Content-Type: application/json

リクエストパラメーター

{
  // ポートフォリオのタイトル
  title: string;

  // ポートフォリオのURL
  url: string;

  // ポートフォリオのリポジトリURL
  repo: string;

  // 0: Webアプリ
  // 1: スマホアプリ
  // 2: デスクトップアプリ
  // 3: スクレイピング
  // 4: ホムペ
  // 5: その他
  type: number;
}

レスポンス

{}


ユーザプロフィール更新

リクエストURL

Put
/v1/update/user

リクエストヘッダー

Authorization: Firebase Authorizationで発行されたjwttoken
Content-Type: application/json

リクエストパラメーター

{
  // ユーザ名
  name: string;

  // GitHubのURL
  githubUrl: string;

  // TwitterのURL
  twitterUrl: string;
}

レスポンス

{}


ポートフォリオ更新

リクエストURL

Put
/v1/update/product

リクエストヘッダー

Authorization: Firebase Authorizationで発行されたjwttoken
Content-Type: application/json

リクエストパラメーター

{
  // ポートフォリオのID
  id: string;

  // ポートフォリオのタイトル
  title: string;

  // ポートフォリオのURL
  url: string;

  // ポートフォリオのリポジトリURL
  repo: string;

  // 0: Webアプリ
  // 1: スマホアプリ
  // 2: デスクトップアプリ
  // 3: スクレイピング
  // 4: ホムペ
  // 5: その他
  type: number;
}

レスポンス

{}


ポートフォリオ削除

リクエストURL

Delete
/v1/cancel/product

リクエストヘッダー

Authorization: Firebase Authorizationで発行されたjwttoken
Content-Type: application/json

リクエストパラメーター

{
  // ポートフォリオのID
  id: string;
}

レスポンス

{}


ユーザプロフィール参照

リクエストURL

Get
/v1/find/user/:uid

リクエストヘッダー

Authorization: null以外の値

リクエストパラメーター

{
  // ユーザID
  uid: string;
}

レスポンス

{
  // ユーザ名
  name: string;

  // TwitterのURL
  twitterUrl: string;

  // GitHubのURL
  githubUrl: string;
}


ページング全投稿(5件)

リクエストURL

Get
/v1/paging/all/:num

リクエストヘッダー

Authorization: null以外の値

リクエストパラメーター

{
  // 1ページ目は1, 2ページ目は2....
  num: string;
}

レスポンス

{
  // 件数
  count: number;
  list: {
    // ポートフォリオのID
    _id: string;

    // 0: Webアプリ
    // 1: スマホアプリ
    // 2: デスクトップアプリ
    // 3: スクレイピング
    // 4: ホムペ
    // 5: その他
    type: number;

    // ポートフォリオのタイトル
    title: string;

    // ポートフォリオのURL
    url: string;

    // ポートフォリオのリポジトリURL
    repo: string;

    // 投稿者名
    name: string[];

    // ユーザID
    uid: string;

    // 作成日
    createdAt: Date;

    // 更新日
    updatedAt: Date;
  }[];
}


ページングポートフォリオのタイトル別(5件)

リクエストURL

Get
/v1/paging/title/:title/:num

リクエストヘッダー

Authorization: null以外の値

リクエストパラメーター

{
  // 1ページ目は1, 2ページ目は2....
  num: string;
  title: string;
}

レスポンス

{
  // 件数
  count: number;
  list: {
    // ポートフォリオのID
    _id: string;

    // 0: Webアプリ
    // 1: スマホアプリ
    // 2: デスクトップアプリ
    // 3: スクレイピング
    // 4: ホムペ
    // 5: その他
    type: number;

    // ポートフォリオのタイトル
    title: string;

    // ポートフォリオのURL
    url: string;

    // ポートフォリオのリポジトリURL
    repo: string;

    // 投稿者名
    name: string[];

    // ユーザID
    uid: string;

    // 作成日
    createdAt: Date;

    // 更新日
    updatedAt: Date;
  }[];
}


ページングタイプ別(5件)

リクエストURL

Get
/v1/paging/type/:type/:num

リクエストヘッダー

Authorization: null以外の値

リクエストパラメーター

{
  // 1ページ目は1, 2ページ目は2....
  num: string;

  // 0: Webアプリ
  // 1: スマホアプリ
  // 2: デスクトップアプリ
  // 3: スクレイピング
  // 4: ホムペ
  // 5: その他
  type: string;
}

レスポンス

{
  // 件数
  count: number;
  list: {
    // ポートフォリオのID
    _id: string;

    // 0: Webアプリ
    // 1: スマホアプリ
    // 2: デスクトップアプリ
    // 3: スクレイピング
    // 4: ホムペ
    // 5: その他
    type: number;

    // ポートフォリオのタイトル
    title: string;

    // ポートフォリオのURL
    url: string;

    // ポートフォリオのリポジトリURL
    repo: string;

    // 投稿者名
    name: string[];

    // ユーザID
    uid: string;

    // 作成日
    createdAt: Date;

    // 更新日
    updatedAt: Date;
  }[];
}


ページングユーザ投稿別

リクエストURL

Get
/v1/paging/user/:uid/:num

リクエストヘッダー

Authorization: null以外の値

リクエストパラメーター

{
  // 1ページ目は1, 2ページ目は2....
  num: string;
  uid: string;
}

レスポンス

{
  // 件数
  count: number;
  list: {
    // ポートフォリオのID
    _id: string;

    // 0: Webアプリ
    // 1: スマホアプリ
    // 2: デスクトップアプリ
    // 3: スクレイピング
    // 4: ホムペ
    // 5: その他
    type: number;

    // ポートフォリオのタイトル
    title: string;

    // ポートフォリオのURL
    url: string;

    // ポートフォリオのリポジトリURL
    repo: string;

    // 投稿者名
    name: string[];

    // ユーザID
    uid: string;

    // 作成日
    createdAt: Date;

    // 更新日
    updatedAt: Date;
  }[];
}


src/route/index.ts
import*asExpressfrom'express';import*asCorsfrom'cors';import*asDotEnvfrom'dotenv';importConstantfrom'src/constant';// route---importcreate_friendfrom'src/route/create/friend';importcreate_userfrom'src/route/create/user';importcreate_productfrom'src/route/create/product';importpaging_allfrom'src/route/paging/all';importpaging_titlefrom'src/route/paging/title';importpaging_typefrom'src/route/paging/type';importpaging_userfrom'src/route/paging/users';importupdate_productfrom'src/route/update/product';importupdate_userfrom'src/route/update/user';importcancel_friendfrom'src/route/cancel/friend';importcancel_productfrom'src/route/cancel/product';importfind_userfrom'src/route/find/user';// route---DotEnv.config();constapp=Express();constrouter=Express.Router();// middleware---app.use(Cors({origin:process.env.ORIGIN_URL}));app.use('/.netlify/functions/api',router);app.use(Express.urlencoded({extended:true}));app.use((req:Express.Request,res:Express.Response,next:Express.NextFunction)=>{req.headers.authorization!==undefined?next():res.sendStatus(403);});app.use((_,__,res:Express.Response,___)=>{res.sendStatus(500);});// middleware---// routing---// ユーザ作成router.post(Constant.API_VERSION+Constant.URL['/create/user'],create_user);// 投稿router.post(Constant.API_VERSION+Constant.URL['/create/product'],create_product);// フォローするrouter.post(Constant.API_VERSION+Constant.URL['/create/friend'],create_friend);// ページング全投稿router.get(Constant.API_VERSION+Constant.URL['/paging/all']+'/:num',paging_all);// ページングタイプ別router.get(Constant.API_VERSION+Constant.URL['/paging/type']+'/:type'+'/:num',paging_type);// ページングタイトル別router.get(Constant.API_VERSION+Constant.URL['/paging/title']+'/:title'+'/:num',paging_title);// ページングユーザ別router.get(Constant.API_VERSION+Constant.URL['/paging/user']+'/:uid'+'/:num',paging_user);// プロフィールrouter.get(Constant.API_VERSION+Constant.URL['/find/user']+'/:uid',find_user);// 更新 ユーザrouter.put(Constant.API_VERSION+Constant.URL['/update/user'],update_user);// 更新 記事router.put(Constant.API_VERSION+Constant.URL['/update/product'],update_product);// フォローはずすrouter.delete(Constant.API_VERSION+Constant.URL['/cancel/friend'],cancel_friend);// 投稿削除router.delete(Constant.API_VERSION+Constant.URL['/cancel/product'],cancel_product);// routing---exportdefaultapp;

Viewing all articles
Browse latest Browse all 8862

Trending Articles