- その1 NestJSやLambdaでのnode.jsの話し
- その2 NestJSのサンプルを動かす、Hello World.
- その3 コントローラーを追加する
- その4 DTOを使う
- その5 CRUD generatorを使い modules,controller, service, dto を生成する
- その6 インターセプター
- その7 ORMでRDBデータベースを扱う
- その8 NestJSをAPI Gateway+AWS Lambdaとしてデプロイ
Controllers(コントローラー)
公式
コントロールはざっくり
- サーバー側としてHttp リクエストを処理する起点となるところ
- リクエストされるURLの部分と処理するclassを紐付けるのはNestJSのフレームワーク側でやってくれる
aws-node-typescript-nest/src/app.controller.ts
をみてみると、
@Controller()
export class AppController {
constructor(private readonly appService: AppService) { }
@Get('hello')
getHello(): string {
return this.appService.getHello();
}
}
-
@Get('hello')
が http://localhost:3000/hello にマッピングされる -
/hello
にリクエストがきたら、@Get('hello')
を指定する getHello() が動作する
Get, Post リクエスト
-
@Get('hello')
は Getメソッドリクエスト対応なので、http://localhost:3000/hello に対して Postメソッドリクエストを送ると対応するHttpメソッドがないため404になります。
404 Not Foundの場合のレスポンス
{
"statusCode": 404,
"error": "Not Found",
"message": "Cannot POST /hello"
}
serverless.yml では、 methdoはanyなのでGetでもPostでも受付る
functions:
main:
handler: src/main.handler
events:
- http:
method: any
path: /{proxy+}
- Postメソッドリクエストで動作するようにするには、以下のようにすれば良い
@Post('hello')
getHello(): string {
return this.appService.getHello();
}
コントローラーの追加
usage:
$ nest g controller [コントローラー名]
実行例
nest g controller cats
以下が生成される
- src/cats/cats.controller.ts
- src/cats/cats.controller.spec.ts ←テスト用ファイル
以下が更新される、追加した catsがmoduleに追加され リクエストを受けるエンドポイントとして認識されるようになる
src/app.module.ts
...
import { CatsController } from './cats/cats.controller';
...
@Module({
imports: [],
controllers: [AppController, CatsController],
providers: [AppService],
})
export class AppModule {}
追加したコントローラーにリクエストを送る
リクエスト
$ curl -X "GET" http://localhost:3000/dev/cats
レスポンス
This action returns all cats
- 末尾のスラッシュ有無では違いなし
$ curl -X "GET" http://localhost:3000/dev/cats
$ curl -X "GET" http://localhost:3000/dev/cats/
追加したコントローラーにリクエストパラメータ付きのリクエストを送る
a=1というパラメータをつける
- CatsControllerでは、
console.log({ request });
を追加してログを出力する
$ curl -X "GET" http://localhost:3000/dev/cats/?a=1
- console.logの内容を確認
[Nest] 2014 - 7/19/2023, 1:07:29 PM [NestApplication] Nest application successfully started +1ms
{
request: <ref *1> IncomingMessage {
_readableState: ReadableState {
objectMode: false,
highWaterMark: 16384,
buffer: BufferList { head: null, tail: null, length: 0 },
length: 0,
pipes: [],
flowing: null,
ended: true,
endEmitted: false,
reading: false,
constructed: true,
sync: true,
needReadable: false,
emittedReadable: false,
readableListening: false,
resumeScheduled: false,
errorEmitted: false,
emitClose: true,
autoDestroy: true,
destroyed: false,
errored: null,
closed: false,
closeEmitted: false,
defaultEncoding: 'utf8',
awaitDrainWriters: null,
multiAwaitDrain: false,
readingMore: true,
dataEmitted: false,
decoder: null,
encoding: null,
[Symbol(kPaused)]: null
},
_events: [Object: null prototype] { end: [Function: clearRequestTimeout] },
_eventsCount: 1,
_maxListeners: undefined,
socket: Socket {
connecting: false,
_hadError: false,
_parent: null,
_host: null,
_readableState: [ReadableState],
_events: [Object: null prototype],
_eventsCount: 8,
_maxListeners: undefined,
_writableState: [WritableState],
allowHalfOpen: true,
_sockname: null,
_pendingData: null,
_pendingEncoding: '',
server: [Server],
_server: [Server],
parser: [HTTPParser],
on: [Function: socketListenerWrap],
addListener: [Function: socketListenerWrap],
prependListener: [Function: socketListenerWrap],
setEncoding: [Function: socketSetEncoding],
_paused: false,
_httpMessage: [ServerResponse],
[Symbol(async_id_symbol)]: 29,
[Symbol(kHandle)]: [Pipe],
[Symbol(lastWriteQueueSize)]: 0,
[Symbol(timeout)]: null,
[Symbol(kBuffer)]: null,
[Symbol(kBufferCb)]: null,
[Symbol(kBufferGen)]: null,
[Symbol(kCapture)]: false,
[Symbol(kSetNoDelay)]: false,
[Symbol(kSetKeepAlive)]: false,
[Symbol(kSetKeepAliveInitialDelay)]: 0,
[Symbol(kBytesRead)]: 0,
[Symbol(kBytesWritten)]: 0,
[Symbol(RequestTimeout)]: undefined
},
httpVersionMajor: 1,
httpVersionMinor: 1,
httpVersion: '1.1',
complete: true,
rawHeaders: [
'Host',
'localhost:3000',
'User-Agent',
'curl/7.74.0',
'Accept',
'*/*',
'x-apigateway-event',
'%7B%22headers%22%3A%7B%22Host%22%3A%22localhost%3A3000%22%2C%22User-Agent%22%3A%22curl%2F7.74.0%22%2C%22Accept%22%3A%22*%2F*%22%7D%2C%22httpMethod%22%3A%22GET%22%2C%22isBase64Encoded%22%3Afalse%2C%22multiValueHeaders%22%3A%7B%22Host%22%3A%5B%22localhost%3A3000%22%5D%2C%22User-Agent%22%3A%5B%22curl%2F7.74.0%22%5D%2C%22Accept%22%3A%5B%22*%2F*%22%5D%7D%2C%22multiValueQueryStringParameters%22%3A%7B%22a%22%3A%5B%221%22%5D%7D%2C%22path%22%3A%22%2Fcats%22%2C%22pathParameters%22%3A%7B%22proxy%22%3A%22cats%22%7D%2C%22queryStringParameters%22%3A%7B%22a%22%3A%221%22%7D%2C%22requestContext%22%3A%7B%22accountId%22%3A%22offlineContext_accountId%22%2C%22apiId%22%3A%22offlineContext_apiId%22%2C%22authorizer%22%3A%7B%22principalId%22%3A%22offlineContext_authorizer_principalId%22%7D%2C%22domainName%22%3A%22offlineContext_domainName%22%2C%22domainPrefix%22%3A%22offlineContext_domainPrefix%22%2C%22extendedRequestId%22%3A%22b411061f-de1b-4be9-a6a4-a982476a36bf%22%2C%22httpMethod%22%3A%22GET%22%2C%22identity%22%3A%7B%22accessKey%22%3Anull%2C%22accountId%22%3A%22offlineContext_accountId%22%2C%22apiKey%22%3A%22offlineContext_apiKey%22%2C%22apiKeyId%22%3A%22offlineContext_apiKeyId%22%2C%22caller%22%3A%22offlineContext_caller%22%2C%22cognitoAuthenticationProvider%22%3A%22offlineContext_cognitoAuthenticationProvider%22%2C%22cognitoAuthenticationType%22%3A%22offlineContext_cognitoAuthenticationType%22%2C%22cognitoIdentityId%22%3A%22offlineContext_cognitoIdentityId%22%2C%22cognitoIdentityPoolId%22%3A%22offlineContext_cognitoIdentityPoolId%22%2C%22principalOrgId%22%3Anull%2C%22sourceIp%22%3A%22127.0.0.1%22%2C%22user%22%3A%22offlineContext_user%22%2C%22userAgent%22%3A%22curl%2F7.74.0%22%2C%22userArn%22%3A%22offlineContext_userArn%22%7D%2C%22path%22%3A%22%2Fcats%22%2C%22protocol%22%3A%22HTTP%2F1.1%22%2C%22requestId%22%3A%2292886438-c883-471c-859b-86b9fa2434ab%22%2C%22requestTime%22%3A%2219%2FJul%2F2023%3A13%3A07%3A28%20%2B0000%22%2C%22requestTimeEpoch%22%3A1689772048766%2C%22resourceId%22%3A%22offlineContext_resourceId%22%2C%22resourcePath%22%3A%22%2Fdev%2F%7Bproxy*%7D%22%2C%22stage%22%3A%22dev%22%7D%2C%22resource%22%3A%22%2F%7Bproxy%2B%7D%22%2C%22stageVariables%22%3Anull%7D',
'x-apigateway-context',
'%7B%22awsRequestId%22%3A%224e598571-6fa8-45fa-b8c3-0d0b5263a1a1%22%2C%22callbackWaitsForEmptyEventLoop%22%3Atrue%2C%22clientContext%22%3Anull%2C%22functionName%22%3A%22serverless-nest-example-dev-main%22%2C%22functionVersion%22%3A%22%24LATEST%22%2C%22invokedFunctionArn%22%3A%22offline_invokedFunctionArn_for_serverless-nest-example-dev-main%22%2C%22logGroupName%22%3A%22offline_logGroupName_for_serverless-nest-example-dev-main%22%2C%22logStreamName%22%3A%22offline_logStreamName_for_serverless-nest-example-dev-main%22%2C%22memoryLimitInMB%22%3A%221024%22%7D',
'Connection',
'close'
],
rawTrailers: [],
aborted: false,
upgrade: false,
url: '/cats?a=1',
method: 'GET',
statusCode: null,
statusMessage: null,
client: Socket {
connecting: false,
_hadError: false,
_parent: null,
_host: null,
_readableState: [ReadableState],
_events: [Object: null prototype],
_eventsCount: 8,
_maxListeners: undefined,
_writableState: [WritableState],
allowHalfOpen: true,
_sockname: null,
_pendingData: null,
_pendingEncoding: '',
server: [Server],
_server: [Server],
parser: [HTTPParser],
on: [Function: socketListenerWrap],
addListener: [Function: socketListenerWrap],
prependListener: [Function: socketListenerWrap],
setEncoding: [Function: socketSetEncoding],
_paused: false,
_httpMessage: [ServerResponse],
[Symbol(async_id_symbol)]: 29,
[Symbol(kHandle)]: [Pipe],
[Symbol(lastWriteQueueSize)]: 0,
[Symbol(timeout)]: null,
[Symbol(kBuffer)]: null,
[Symbol(kBufferCb)]: null,
[Symbol(kBufferGen)]: null,
[Symbol(kCapture)]: false,
[Symbol(kSetNoDelay)]: false,
[Symbol(kSetKeepAlive)]: false,
[Symbol(kSetKeepAliveInitialDelay)]: 0,
[Symbol(kBytesRead)]: 0,
[Symbol(kBytesWritten)]: 0,
[Symbol(RequestTimeout)]: undefined
},
_consuming: false,
_dumped: false,
next: [Function: next],
baseUrl: '',
originalUrl: '/cats?a=1',
_parsedUrl: Url {
protocol: null,
slashes: null,
auth: null,
host: null,
port: null,
hostname: null,
hash: null,
search: '?a=1',
query: 'a=1',
pathname: '/cats',
path: '/cats?a=1',
href: '/cats?a=1',
_raw: '/cats?a=1'
},
params: {},
query: { a: '1' },
res: ServerResponse {
_events: [Object: null prototype],
_eventsCount: 1,
_maxListeners: undefined,
outputData: [],
outputSize: 0,
writable: true,
destroyed: false,
_last: false,
chunkedEncoding: false,
shouldKeepAlive: false,
maxRequestsOnConnectionReached: false,
_defaultKeepAlive: true,
useChunkedEncodingByDefault: true,
sendDate: true,
_removedConnection: false,
_removedContLen: false,
_removedTE: false,
_contentLength: null,
_hasBody: true,
_trailer: '',
finished: false,
_headerSent: false,
_closed: false,
socket: [Socket],
_header: null,
_keepAliveTimeout: 5000,
_onPendingData: [Function: bound updateOutgoingData],
req: [Circular *1],
_sent100: false,
_expect_continue: false,
locals: [Object: null prototype] {},
[Symbol(kCapture)]: false,
[Symbol(kNeedDrain)]: false,
[Symbol(corked)]: 0,
[Symbol(kOutHeaders)]: [Object: null prototype],
[Symbol(kUniqueHeaders)]: null
},
apiGateway: { event: [Object], context: [Object] },
body: {},
route: Route { path: '/cats', stack: [Array], methods: [Object] },
[Symbol(kCapture)]: false,
[Symbol(kHeaders)]: {
host: 'localhost:3000',
'user-agent': 'curl/7.74.0',
accept: '*/*',
connection: 'close'
},
[Symbol(kHeadersCount)]: 12,
[Symbol(kTrailers)]: null,
[Symbol(kTrailersCount)]: 0,
[Symbol(RequestTimeout)]: undefined
}
}
(λ: main) RequestId: 4e598571-6fa8-45fa-b8c3-0d0b5263a1a1 Duration: 271.70 ms Billed Duration: 272 ms
a=あいうえお というパラメータをつける
- リクエストする時にはURLエンコードを行う
$ curl -G http://localhost:3000/dev/cats/ --data-urlencode "a=あいうえお"
- console.logの内容を確認
ANY /dev/cats (λ: main)
{
request: <ref *1> IncomingMessage {
_readableState: ReadableState {
objectMode: false,
highWaterMark: 16384,
buffer: BufferList { head: null, tail: null, length: 0 },
length: 0,
pipes: [],
flowing: null,
ended: true,
endEmitted: false,
reading: false,
constructed: true,
sync: true,
needReadable: false,
emittedReadable: false,
readableListening: false,
resumeScheduled: false,
errorEmitted: false,
emitClose: true,
autoDestroy: true,
destroyed: false,
errored: null,
closed: false,
closeEmitted: false,
defaultEncoding: 'utf8',
awaitDrainWriters: null,
multiAwaitDrain: false,
readingMore: true,
dataEmitted: false,
decoder: null,
encoding: null,
[Symbol(kPaused)]: null
},
_events: [Object: null prototype] { end: [Function: clearRequestTimeout] },
_eventsCount: 1,
_maxListeners: undefined,
socket: Socket {
connecting: false,
_hadError: false,
_parent: null,
_host: null,
_readableState: [ReadableState],
_events: [Object: null prototype],
_eventsCount: 8,
_maxListeners: undefined,
_writableState: [WritableState],
allowHalfOpen: true,
_sockname: null,
_pendingData: null,
_pendingEncoding: '',
server: [Server],
_server: [Server],
parser: [HTTPParser],
on: [Function: socketListenerWrap],
addListener: [Function: socketListenerWrap],
prependListener: [Function: socketListenerWrap],
setEncoding: [Function: socketSetEncoding],
_paused: false,
_httpMessage: [ServerResponse],
[Symbol(async_id_symbol)]: 159,
[Symbol(kHandle)]: [Pipe],
[Symbol(lastWriteQueueSize)]: 0,
[Symbol(timeout)]: null,
[Symbol(kBuffer)]: null,
[Symbol(kBufferCb)]: null,
[Symbol(kBufferGen)]: null,
[Symbol(kCapture)]: false,
[Symbol(kSetNoDelay)]: false,
[Symbol(kSetKeepAlive)]: false,
[Symbol(kSetKeepAliveInitialDelay)]: 0,
[Symbol(kBytesRead)]: 0,
[Symbol(kBytesWritten)]: 0,
[Symbol(RequestTimeout)]: undefined
},
httpVersionMajor: 1,
httpVersionMinor: 1,
httpVersion: '1.1',
complete: true,
rawHeaders: [
'Host',
'localhost:3000',
'User-Agent',
'curl/7.74.0',
'Accept',
'*/*',
'x-apigateway-event',
'%7B%22headers%22%3A%7B%22Host%22%3A%22localhost%3A3000%22%2C%22User-Agent%22%3A%22curl%2F7.74.0%22%2C%22Accept%22%3A%22*%2F*%22%7D%2C%22httpMethod%22%3A%22GET%22%2C%22isBase64Encoded%22%3Afalse%2C%22multiValueHeaders%22%3A%7B%22Host%22%3A%5B%22localhost%3A3000%22%5D%2C%22User-Agent%22%3A%5B%22curl%2F7.74.0%22%5D%2C%22Accept%22%3A%5B%22*%2F*%22%5D%7D%2C%22multiValueQueryStringParameters%22%3A%7B%22a%22%3A%5B%22%E3%81%82%E3%81%84%E3%81%86%E3%81%88%E3%81%8A%22%5D%7D%2C%22path%22%3A%22%2Fcats%22%2C%22pathParameters%22%3A%7B%22proxy%22%3A%22cats%22%7D%2C%22queryStringParameters%22%3A%7B%22a%22%3A%22%E3%81%82%E3%81%84%E3%81%86%E3%81%88%E3%81%8A%22%7D%2C%22requestContext%22%3A%7B%22accountId%22%3A%22offlineContext_accountId%22%2C%22apiId%22%3A%22offlineContext_apiId%22%2C%22authorizer%22%3A%7B%22principalId%22%3A%22offlineContext_authorizer_principalId%22%7D%2C%22domainName%22%3A%22offlineContext_domainName%22%2C%22domainPrefix%22%3A%22offlineContext_domainPrefix%22%2C%22extendedRequestId%22%3A%22c264f026-ebf6-4d0c-9163-34e44e3de765%22%2C%22httpMethod%22%3A%22GET%22%2C%22identity%22%3A%7B%22accessKey%22%3Anull%2C%22accountId%22%3A%22offlineContext_accountId%22%2C%22apiKey%22%3A%22offlineContext_apiKey%22%2C%22apiKeyId%22%3A%22offlineContext_apiKeyId%22%2C%22caller%22%3A%22offlineContext_caller%22%2C%22cognitoAuthenticationProvider%22%3A%22offlineContext_cognitoAuthenticationProvider%22%2C%22cognitoAuthenticationType%22%3A%22offlineContext_cognitoAuthenticationType%22%2C%22cognitoIdentityId%22%3A%22offlineContext_cognitoIdentityId%22%2C%22cognitoIdentityPoolId%22%3A%22offlineContext_cognitoIdentityPoolId%22%2C%22principalOrgId%22%3Anull%2C%22sourceIp%22%3A%22127.0.0.1%22%2C%22user%22%3A%22offlineContext_user%22%2C%22userAgent%22%3A%22curl%2F7.74.0%22%2C%22userArn%22%3A%22offlineContext_userArn%22%7D%2C%22path%22%3A%22%2Fcats%22%2C%22protocol%22%3A%22HTTP%2F1.1%22%2C%22requestId%22%3A%222acb3594-7a11-439a-a451-7bd8478e84c6%22%2C%22requestTime%22%3A%2219%2FJul%2F2023%3A13%3A21%3A44%20%2B0000%22%2C%22requestTimeEpoch%22%3A1689772904950%2C%22resourceId%22%3A%22offlineContext_resourceId%22%2C%22resourcePath%22%3A%22%2Fdev%2F%7Bproxy*%7D%22%2C%22stage%22%3A%22dev%22%7D%2C%22resource%22%3A%22%2F%7Bproxy%2B%7D%22%2C%22stageVariables%22%3Anull%7D',
'x-apigateway-context',
'%7B%22awsRequestId%22%3A%228ef4d650-7c2f-4c57-ba0b-9504e0160346%22%2C%22callbackWaitsForEmptyEventLoop%22%3Atrue%2C%22clientContext%22%3Anull%2C%22functionName%22%3A%22serverless-nest-example-dev-main%22%2C%22functionVersion%22%3A%22%24LATEST%22%2C%22invokedFunctionArn%22%3A%22offline_invokedFunctionArn_for_serverless-nest-example-dev-main%22%2C%22logGroupName%22%3A%22offline_logGroupName_for_serverless-nest-example-dev-main%22%2C%22logStreamName%22%3A%22offline_logStreamName_for_serverless-nest-example-dev-main%22%2C%22memoryLimitInMB%22%3A%221024%22%7D',
'Connection',
'close'
],
rawTrailers: [],
aborted: false,
upgrade: false,
url: '/cats?a=%E3%81%82%E3%81%84%E3%81%86%E3%81%88%E3%81%8A',
method: 'GET',
statusCode: null,
statusMessage: null,
client: Socket {
connecting: false,
_hadError: false,
_parent: null,
_host: null,
_readableState: [ReadableState],
_events: [Object: null prototype],
_eventsCount: 8,
_maxListeners: undefined,
_writableState: [WritableState],
allowHalfOpen: true,
_sockname: null,
_pendingData: null,
_pendingEncoding: '',
server: [Server],
_server: [Server],
parser: [HTTPParser],
on: [Function: socketListenerWrap],
addListener: [Function: socketListenerWrap],
prependListener: [Function: socketListenerWrap],
setEncoding: [Function: socketSetEncoding],
_paused: false,
_httpMessage: [ServerResponse],
[Symbol(async_id_symbol)]: 159,
[Symbol(kHandle)]: [Pipe],
[Symbol(lastWriteQueueSize)]: 0,
[Symbol(timeout)]: null,
[Symbol(kBuffer)]: null,
[Symbol(kBufferCb)]: null,
[Symbol(kBufferGen)]: null,
[Symbol(kCapture)]: false,
[Symbol(kSetNoDelay)]: false,
[Symbol(kSetKeepAlive)]: false,
[Symbol(kSetKeepAliveInitialDelay)]: 0,
[Symbol(kBytesRead)]: 0,
[Symbol(kBytesWritten)]: 0,
[Symbol(RequestTimeout)]: undefined
},
_consuming: false,
_dumped: false,
next: [Function: next],
baseUrl: '',
originalUrl: '/cats?a=%E3%81%82%E3%81%84%E3%81%86%E3%81%88%E3%81%8A',
_parsedUrl: Url {
protocol: null,
slashes: null,
auth: null,
host: null,
port: null,
hostname: null,
hash: null,
search: '?a=%E3%81%82%E3%81%84%E3%81%86%E3%81%88%E3%81%8A',
query: 'a=%E3%81%82%E3%81%84%E3%81%86%E3%81%88%E3%81%8A',
pathname: '/cats',
path: '/cats?a=%E3%81%82%E3%81%84%E3%81%86%E3%81%88%E3%81%8A',
href: '/cats?a=%E3%81%82%E3%81%84%E3%81%86%E3%81%88%E3%81%8A',
_raw: '/cats?a=%E3%81%82%E3%81%84%E3%81%86%E3%81%88%E3%81%8A'
},
params: {},
query: { a: 'あいうえお' },
res: ServerResponse {
_events: [Object: null prototype],
_eventsCount: 1,
_maxListeners: undefined,
outputData: [],
outputSize: 0,
writable: true,
destroyed: false,
_last: false,
chunkedEncoding: false,
shouldKeepAlive: false,
maxRequestsOnConnectionReached: false,
_defaultKeepAlive: true,
useChunkedEncodingByDefault: true,
sendDate: true,
_removedConnection: false,
_removedContLen: false,
_removedTE: false,
_contentLength: null,
_hasBody: true,
_trailer: '',
finished: false,
_headerSent: false,
_closed: false,
socket: [Socket],
_header: null,
_keepAliveTimeout: 5000,
_onPendingData: [Function: bound updateOutgoingData],
req: [Circular *1],
_sent100: false,
_expect_continue: false,
locals: [Object: null prototype] {},
[Symbol(kCapture)]: false,
[Symbol(kNeedDrain)]: false,
[Symbol(corked)]: 0,
[Symbol(kOutHeaders)]: [Object: null prototype],
[Symbol(kUniqueHeaders)]: null
},
apiGateway: { event: [Object], context: [Object] },
body: {},
route: Route { path: '/cats', stack: [Array], methods: [Object] },
[Symbol(kCapture)]: false,
[Symbol(kHeaders)]: {
host: 'localhost:3000',
'user-agent': 'curl/7.74.0',
accept: '*/*',
connection: 'close'
},
[Symbol(kHeadersCount)]: 12,
[Symbol(kTrailers)]: null,
[Symbol(kTrailersCount)]: 0,
[Symbol(RequestTimeout)]: undefined
}
}
(λ: main) RequestId: 8ef4d650-7c2f-4c57-ba0b-9504e0160346 Duration: 3.44 ms Billed Duration: 4 ms
ログ内の
%E3%81%82%E3%81%84%E3%81%86%E3%81%88%E3%81%8A
をデコードすると、「あいうえお」になる
また、ログ内の以下ではGETをデコード済である
query: { a: 'あいうえお' },
なお、以下のようにURLエンコードしないと文字化けします
curl -X "GET" http://localhost:3000/dev/cats/?a=あいうえお
- 文字化け時のconsole.logの結果を一部抜粋
url: '/cats?a=%C3%A3%C2%81%C2%82%C3%A3%C2%81%C2%84%C3%A3%C2%81%C2%86%C3%A3%C2%81%C2%88%C3%A3%C2%81%C2%8A',
method: 'GET',
originalUrl: '/cats?a=%C3%A3%C2%81%C2%82%C3%A3%C2%81%C2%84%C3%A3%C2%81%C2%86%C3%A3%C2%81%C2%88%C3%A3%C2%81%C2%8A',
URLエンコードされたと仮定の想定箇所では
a=%C3%A3%C2%81%C2%82%C3%A3%C2%81%C2%84%C3%A3%C2%81%C2%86%C3%A3%C2%81%C2%88%C3%A3%C2%81%C2%8A
となっており、デコードすると以下のように文字化けしてますね・・・
ããããã
ちょっと寄り道
REST API
-
上記で Get,Postについて記載したが、あくまでもHTTPメソッドに対しての指定方法の説明で、REST APIではない
-
Web API 全般
- ざっくりクライアントとサーバのやりとり
- Web上に公開されていて、外部から呼び出して利用が可能なAPI、SOAPとREST
- URIの考え方がオレオレもある
- /User/create
- /User/update
- /User/delete
- /CreateUser
- /UpdateUser
- /DeleteUser
-
REST APIは
- リソース、リソースの参照、リソース操作が統一されている
- 各リソースに対してGET, POST, PUT, DELETEでリクエストを送信し、レスポンスをXMLやjsonなどで受け取る
- RESTの原則(大きく4つ)に従って実装されているシステムのことを、RESTfulと呼びます。
- Getメソッド データ取得
- POSTメソッド データ作成
- PUTメソッド データ更新
- DELETEメソッド データ削除
- /usersの場合を例にすると
- /users のGet ユーザー一覧
- /users のPost ユーザー作成
- /users のPut ユーザー更新
- /users のDelete ユーザーさくっと
-
GraphQLだとエンドポイント1つで、複数メソッドのような動きなのでRESTとは別なので混同しないようにする
-
例えば、GraphQLの AWS AppSync の定義 schema.graphql が以下で、エンドポイント1つで操作はクエリーとして提供する。例 https://gist.github.com/ssugimoto/83acb96bce057043bc80c7c43fe7c2a0
type Query {
getNode: [Note]
}
type Mutation {
addNote(id: ID!, name: String, description: String, image: String): Note
}
type Note @model {
id: ID!
name: String!
description: String
image: String
}