ããã«ã¡ã¯ãã¨ã³ã¸ãã¢ãªã³ã°ã°ã«ã¼ãã®ç¦æ (@fukubaya) ã§ãã
å æãããä»å¹´ã®ç§ãããã«ãªãªã¼ã¹äºå®ã®æ°ãµã¼ãã¹ã®è¨è¨ãéçºãå§ãã¾ããã ãã£ããæ°ããå§ãããµã¼ãã¹ãªã®ã§ãã¾ã çµé¨ãããã¨ããªãè¨èªããã¬ã¼ã ã¯ã¼ã¯ãæè¡ã使ããªãã¨æ¥½ããããã¾ããã
ããã§ãããã¯ã¨ã³ãã«Goã«ãã¦ãããã³ãã®APIã¾ã§å«ãã¦gRPCã® .proto
ãã¡ã¤ã«ã§å®ç¾©ãä¸å
åããAPIã³ã¼ã㯠protoc
ã§çæãããè¨ç»ãç«ã¦ã¦ããã®ã§ããã
- ããã³ãã§gRPCã¨ãªãã¨ã gRPC-web ã grpc-gateway ã«ãªããããªãªã¼ã¹ã¾ã§ã«ä½¿ããæéã§ã¯èªè¨¼ãå«ããã¨æ¤è¨¼ãéã«åããªããã
- Goã ãã§ãªããterraform(ã¤ã³ãã©è¨è¨ãããã¾ã) ã Vue.jsãä»åãåãã¦ãã¨ããã¡ã³ãã¼ããããããã«RESTã§ã¯ãªãgRPCããã¨ãªãã¨æªçµé¨æè¡ãå¤ããã¦ãã£ããã¢ããã追ãã¤ããªããã
ã¨ãããã¨ããã£ã¦è¦éãã¾ããã
ããã§ãä½ãããæ°ãã触ãè¨èªããã¬ã¼ã ã¯ã¼ã¯ã¯å ¥ãããã®ã§ãä»å㯠ããã¯ã¨ã³ãã«SpringBoot(Kotlin)ããããã³ãã¨ã³ãã« Vue.js(Typescript)ã使ããã¨ã«ãªãã¾ãã*1ãgRPCã¯è¦éã£ãã®ã§ãRESTã®APIå®ç¾©ä¸å åãã³ã¼ãèªåçæãå®ç¾ããããOpenAPIãå©ç¨ãããã¨ã«ãã¾ããã
OpenAPI
OpenAPI Specificationã¯ãREST APIã®ããã®APIå®ç¾©ãã©ã¼ãããã§ãã å ã ã¯Swaggerããã¹ã¿ã¼ããããã®ã§ããã2016å¹´ããåé¢ãã¦OpenAPIã¨ãªãã¾ããã 2017å¹´7æã«3.0ããªãªã¼ã¹ããã¦ãã¾ãã
APIå®ç¾©ã®è¨è¿°
APIå®ç¾©ã¯YAMLãJSONã§æ¸ãã¾ãã å®ç¾©ãæ·±ããªãã¨YAMLããJSONã®æ¹ãæ¸ããããã§ãããJSONã ã¨ã³ã¡ã³ããæ¸ããªãã®ã§ãã©ã¡ããä¸é·ä¸çã§ãã ä»åã¯YAMLã®ä¾ã示ãã¾ãã
å®ç¾©ã®ä½æã«ã¯ãSwaggerãæä¾ãã¦ããEditorã便å©ã§ãã dockerã§èµ·åãããã®ãããã¾ããããªã³ã©ã¤ã³ã§ã試ãã¾ãã
docker run -d -p 80:8080 swaggerapi/swagger-editor
OpenAPIã®è©³ç´°ä»æ§ã¯ä»¥ä¸ãåèã«ã
paths
APIã®endpointã¨ãå¿ è¦ãªãã©ã¡ã¼ã¿ãã¬ã¹ãã³ã¹ãå®ç¾©ãã¾ãã 以ä¸ã¯ãGETã§ãªã¹ããè¿ãä¾ã§ãã
paths: /todos/list: get: tags: - todos summary: TODOã®ãªã¹ã operationId: listTodos parameters: - name: offset in: query description: offset (ããã©ã«ã0) required: false schema: type: integer format: int32 - name: limit in: query description: åå¾ä»¶æ° (ããã©ã«ã10) required: false schema: type: integer format: int32 responses: '200': description: TODOã®ãªã¹ãè¿ã content: application/json: schema: $ref: "#/components/schemas/ApiTodos"
queryãã©ã¡ã¼ã¿ã ãã§ãªããpathãã©ã¡ã¼ã¿ãæå®ã§ãã¾ãã
paths: /todos/{id}: get: tags: - todos summary: TODOã®åå¾ operationId: getTodo parameters: - name: id in: path description: ID required: true schema: type: integer format: int32 responses: '200': description: TODOãè¿ã content: application/json: schema: $ref: "#/components/schemas/ApiTodo"
ãã¡ããPOSTãªã©GET以å¤ã®APIãå®ç¾©ã§ãã¾ãã
paths: /todos/post: post: tags: - todos summary: æ°ãã«TODOã追å ãã operationId: postTodo requestBody: required: true content: application/json: schema: $ref: "#/components/schemas/ApiTodoPostRequest" responses: '201': description: ä½æå®äº
å®ç¾©ãæ¸ãã¦ããä¸ã§ä»¥ä¸2ç¹ãåããã«ããã£ãã®ã§è£è¶³ãã¦ããã¾ãã
tags
: APIãã°ã«ã¼ãåããããã«æå®ãã¾ããä¸è¨ã®ä¾ã§ã¯å ¨ã¦todosã°ã«ã¼ãã«ã¾ã¨ãããã¾ããçç¥ããã¨ãã¹ã¦default
ã«ããã¦ãã¾ãã®ã§ãä½ãã¯æå®ããæ¹ãããã§ããoperationId
: APIã«ããæä½(operation)ãèå¥ããããã®IDã§ãã大æåå°æåã¯åºå¥ããã¾ããå¾ã«çæããã³ã¼ãã§ãã®IDããã®ã¾ã¾ã¡ã½ããåã¨ãã¦ä½¿ãããã®ã§ããããæèããå½åã«ããã¨ããã§ãã
components
APIå®ç¾©ã§ç¹°ãè¿ãåºç¾ãããããªåå©ç¨å¯è½ãªobjectãå®ç¾©ãã¾ãã
ä¸è¨ã®ä¾ã§ã¯ ApiTodo
, ApiTodos
, ApiTodoPostRequest
ãªã©ã $ref
ã§åç
§ãã¦ãããã®ã§ãã
以ä¸ã®ä¾ã§ã¯ schemas
ã ãã示ãã¾ããã parameters
ã responses
ãå®ç¾©ã§ãã¾ãã
APIå°ç¨ã®Schemaã§ãããã¨ã強調ããããããã¹ã¦ Api
ãå
é ã«ã¤ãã¦ãã¾ã*2ã
components: schemas: ApiTodo: description: TODO required: - id - title properties: id: description: ID type: integer format: int32 title: description: ã¿ã¤ãã« type: string ApiTodos: description: TODOã®ãªã¹ã type: array items: $ref: "#/components/schemas/ApiTodo" ApiTodoPostRequest: description: TODOçæãªã¯ã¨ã¹ã required: - title properties: title: description: title type: string
ããã ãã§APIå®ç¾©ã¯å®æã§ãã
ã³ã¼ãã®çæ
ã³ã¼ãã®çæã¯OpenAPI Generatorã§è¡ãã¾ãã
# npm % npm install @openapitools/openapi-generator-cli -g # Homebrew % brew install openapi-generator
対å¿ãã¦ããè¨èªã®ä¸è¦§ã¯ããã«ããã¾ãã åãè¨èªã§ããã¬ã¼ã ã¯ã¼ã¯ãã¨ã«å¥ã®Generatorãç¨æããã¦ããè¨èªãããã¾ãã
ããã³ãä¾: typescript-axios
typescript
ã§é信㯠axios
ã使ãã³ã¼ããçæãã¦ã¿ã¾ãã
% openapi-generator generate \ -g typescript-axios \ # çæããè¨èª -i ./api.yml \ # APIå®ç¾© -o ./ts \ # åºåå ãã£ã¬ã¯ã㪠--api-package=api \ # APIå®ç¾©ã®ãã£ã¬ã¯ã㪠--model-package=model \ # ã¢ãã«(schema)å®ç¾©ã®ãã£ã¬ã¯ã㪠--additional-properties=withSeparateModelsAndApi=true # è¨èªãã¨ã«æå®ã§ãããã©ã¡ã¼ã¿(ã¢ãã«ã¨APIãå¥ãã¡ã¤ã«ã«ãã) % tree ts ts âââ api â  âââ todos-api.ts âââ api.ts âââ base.ts âââ configuration.ts âââ custom.d.ts âââ git_push.sh âââ index.ts âââ model âââ api-error.ts âââ api-todo-post-request.ts âââ api-todo.ts âââ index.ts
ApiTodo
ã¯ä»¥ä¸ã®ããã«çæããã¾ãã(å¾ã§èª¬æããæ¹æ³ã§ãã©ã¼ããããã¦ããã¾ã)ã
ãã¡ããåãããã¾ãã
/** * TODO * @export * @interface ApiTodo */ export interface ApiTodo { /** * ID * @type {number} * @memberof ApiTodo */ id: number; /** * ã¿ã¤ãã« * @type {string} * @memberof ApiTodo */ title: string; }
APIã¯ã©ã¤ã¢ã³ããåããçæããã¾ãã
/** * TodosApi - object-oriented interface * @export * @class TodosApi * @extends {BaseAPI} */ export class TodosApi extends BaseAPI { /** * * @summary TODOã®åå¾ * @param {number} id ID * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof TodosApi */ public getTodo(id: number, options?: any) { return TodosApiFp(this.configuration).getTodo(id, options)( this.axios, this.basePath ); } /** * * @summary TODOã®ãªã¹ã * @param {number} [offset] offset (ããã©ã«ã0) * @param {number} [limit] åå¾ä»¶æ° (ããã©ã«ã10) * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof TodosApi */ public listTodos(offset?: number, limit?: number, options?: any) { return TodosApiFp(this.configuration).listTodos(offset, limit, options)( this.axios, this.basePath ); } /** * * @summary æ°ãã«TODOã追å ãã * @param {ApiTodoPostRequest} apiTodoPostRequest * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof TodosApi */ public postTodo(apiTodoPostRequest: ApiTodoPostRequest, options?: any) { return TodosApiFp(this.configuration).postTodo(apiTodoPostRequest, options)( this.axios, this.basePath ); } }
çæãããã³ã¼ãèªä½ã¯importããã ãã§ããã®ã§ãå ã®ã³ã¼ãã触ããã¨ãªãå©ç¨ã§ãã¾ãã
import { AxiosResponse } from "axios"; import { ApiTodo } from "./ts/model/api-todo"; import { TodosApi } from "./ts/api/todos-api"; let client: TodosApi = new TodosApi(); client.listTodos(0, 10) .then((res: AxiosResponse<ApiTodo[]>)=> { let todos: Array<ApiTodo> = res.data; for (const t of todos) { console.log(t); } }) .catch((error: any) => { console.log(error); });
ããã¯ã¨ã³ãä¾: kotlin-spring
SpringBootåãã®Kotlinã®ã³ã¼ããçæãã¦ã¿ã¾ãã
% openapi-generator generate \ -g kotlin-spring \ -i ./api.yml \ -o . \ --additional-properties=library=spring-boot,basePackage=com.m3.todo,apiSuffix=ApiController,gradleBuildFile=false,serviceInterface=true \ --api-package=com.m3.todo.adapter.restapi.controller \ --model-package=com.m3.todo.adapter.restapi.model % tree src src âââ main â  âââ kotlin â  â  âââ com â  â  âââ m3 â  â  âââ todo â  â  âââ Application.kt â  â  âââ adapter â  â  âââ restapi â  â  âââ controller â  â  â  âââ Exceptions.kt â  â  â  âââ TodosApiController.kt â  â  â  âââ TodosApiControllerService.kt â  â  âââ model â  â  âââ ApiError.kt â  â  âââ ApiTodo.kt â  â  âââ ApiTodoPostRequest.kt â  âââ resources â  âââ application.yaml âââ test âââ kotlin âââ com âââ m3 âââ todo âââ adapter âââ restapi âââ controller âââ TodosApiControllerTest.kt
ApiTodo
㯠data class
ã¨ãã¦å®ç¾©ããã¾ããã
package com.m3.todo.adapter.restapi.model import com.fasterxml.jackson.annotation.JsonProperty import javax.validation.constraints.NotNull /** * TODO * @param id ID * @param title ã¿ã¤ãã« */ data class ApiTodo( @get:NotNull @JsonProperty("id") val id: kotlin.Int, @get:NotNull @JsonProperty("title") val title: kotlin.String )
controller㯠interface
ã¨ãã¦å®ç¾©ããã TodosApiControllerService
ã injection ããæ§æã«ãã¾ããã
ããã©ã«ãã®è¨å®ã 㨠interface
ã«ãªããªãã®ã§ãçææã« serviceInterface=true
ãæå®ãã¦ãã¾ãã
interface
ã«ãããã¨ã§ããã® controller èªä½ã¯è§¦ãããå¥é TodosApiControllerService
ã®å®è£
ãæ¸ãã ãã§ãããªãã¾ãã
package com.m3.todo.adapter.restapi.controller ... @RestController @Validated @RequestMapping("\${api.base-path:/api/v1}") class TodosApiControllerController(@Autowired(required = true) val service: TodosApiControllerService) { @RequestMapping( value = ["/todos/{id}"], produces = ["application/json"], method = [RequestMethod.GET]) fun getTodo( @PathVariable("id") id: kotlin.Int ): ResponseEntity<ApiTodo> { return ResponseEntity(service.getTodo(id), HttpStatus.valueOf(200)) } @RequestMapping( value = ["/todos/list"], produces = ["application/json"], method = [RequestMethod.GET]) fun listTodos( @RequestParam(value = "offset", required = false) offset: kotlin.Int?, @RequestParam(value = "limit", required = false) limit: kotlin.Int? ): ResponseEntity<List<ApiTodo>> { return ResponseEntity(service.listTodos(offset, limit), HttpStatus.valueOf(200)) } @RequestMapping( value = ["/todos/post"], produces = ["application/json"], consumes = ["application/json"], method = [RequestMethod.POST]) fun postTodo( @Valid @RequestBody apiTodoPostRequest: ApiTodoPostRequest ): ResponseEntity<Unit> { return ResponseEntity(service.postTodo(apiTodoPostRequest), HttpStatus.valueOf(201)) } }
å°ã£ããã¨ã¨å¯¾å¦
ããã ãã§çæã¯ã§ããã®ã§ãããç´°ããã¨ããã§å°ãã¾ããã
çæããããã¡ã¤ã«ãã³ã¼ããã©ã¼ãããã«ã¼ã«ã«åããªã
çææã®ã¡ãã»ã¼ã¸ã«ã表示ããã¦ãã¾ãããçæå¾ã®å¦çã§ã³ã¼ãã®ãã©ã¼ããããããããã¨ãã§ãã¾ãã OpenAPI Generatorãçæããã³ã¼ãã¯åºæ¬çã« mustache ã«ãããã³ãã¬ã¼ãã§çæããã¦ããã®ã§ã å¿ ããããã©ã¼ããããåè¨èªã§æ¨å¥¨ãããã«ã¼ã«ã«å¾ã£ã¦ãã訳ã§ã¯ããã¾ããã ããã§ãç°å¢å¤æ°ã¨ãªãã·ã§ã³ã§çæå¾ã®å¦çãå¥éæå®ãã¾ãã
typescript-axios
ã®å ´åã
# å¦çå¾ã« prettier ã§ãã©ã¼ããã % export TS_POST_PROCESS_FILE="$(which prettier) --write" % openapi-generator generate \ -g typescript-axios \ -i ./api.yml \ -o ./ts \ --api-package=api \ --model-package=model \ --additional-properties=withSeparateModelsAndApi=true \ --enable-post-process-file # çæå¾ã®å¦çãå®è¡ãã
kotlin-spring
ã®å ´åã
# å¦çå¾ã« ktlint ã§ãã©ã¼ããã % export KOTLIN_POST_PROCESS_FILE="$(which ktlint) -F" % openapi-generator generate \ -g kotlin-spring \ -i ./api.yml \ -o . \ --additional-properties=library=spring-boot,basePackage=com.m3.todo,apiSuffix=ApiController,gradleBuildFile=false,serviceInterface=true \ --api-package=com.m3.todo.adapter.restapi.controller \ --model-package=com.m3.todo.adapter.restapi.model \ --enable-post-process-file # çæå¾ã®å¦çãå®è¡ãã
ãããªããã¡ã¤ã«ãçæããã
generatorãæ°ãå©ããã¦æ¬ä½ã³ã¼ã以å¤ã«ãã¡ã¤ã«ãçæãã¾ãããä¸è¦ãªãã¡ã¤ã«ãããã¾ãã ãã®ãããæåã§ä½ã£ããã¡ã¤ã«ãä¸æ¸ãããã¦ãã¾ã£ãããã¾ãã
çæãä¸è¦ãªãã¡ã¤ã«ã¯ .openapi-generator-ignore
ã«åæãã¦ãåºåå
ã®ãã£ã¬ã¯ããªã®ãããã«ç½®ãã¦ããã¨
çæãããªããªãã¾ãã
typescript-axios
ã®å ´åã®ä¾ã
# OpenAPI Generator Ignore # Generated by openapi-generator https://github.com/openapitools/openapi-generator # Use this file to prevent files from being overwritten by the generator. # The patterns follow closely to .gitignore or .dockerignore. git_push.sh
kotlin-spring
ã®å ´åã®ä¾ã
# OpenAPI Generator Ignore # Generated by openapi-generator https://github.com/openapitools/openapi-generator # Use this file to prevent files from being overwritten by the generator. # The patterns follow closely to .gitignore or .dockerignore. README.md pom.xml build.gradle.kts build.gradle settings.gradle src/main/resources/application.yaml src/main/kotlin/com/m3/todo/Application.kt src/test/
çæãã¡ã¤ã«ãã«ã¹ã¿ãã¤ãºããã
çæããããã¡ã¤ã«ãã«ã¹ã¿ãã¤ãºãããå ´åãããã¾ãã ä¾ãã°ããã¹ã¦ã®Controllerã§å ±éçãªå¦çãæã¿ããããªã©ã§ãã
å ã»ã©å°ã触ããããã«ãã³ã¼ãã¯mustacheã®ãã³ãã¬ã¼ãã§çæãã¦ããã®ã§ã ãã®ãã³ãã¬ã¼ããã¤ã¸ãã°ããç¨åº¦ã«ã¹ã¿ãã¤ãºãå¯è½ã§ãã
ã¾ãã¯ãªãªã¸ãã«ã®ãã³ãã¬ã¼ãããã£ã¬ã¯ããªãã¨ã³ãã¼ãã¦ããã¾ãã
ä¾ãã°ãAPIå¦çã®åã«å¿
ããã°ãè¨é²ããå¦çãå
¥ãã¦ã¿ã¾ã*3ã kotlin-spring/api.mustache
ãç·¨éãã¾ãã
4a5 > import com.m3.todo.base.service.LoggingService 31a33 > import javax.service.http.HttpServletRequest 60c62 < class {{classname}}Controller({{#serviceInterface}}@Autowired(required = true) val service: {{classname}}Service{{/serviceInterface}}) { --- > class {{classname}}Controller({{#serviceInterface}}@Autowired val loggingSerice: LoggingService, @Autowired(required = true) val service: {{classname}}Service{{/serviceInterface}}) { 80c82,83 < {{#reactive}}{{^isListContainer}}suspend {{/isListContainer}}{{/reactive}}fun {{operationId}}({{#allParams}}{{>queryParams}}{{>pathParams}}{{>headerParams}}{{>bodyParams}}{{>formParams}}{{#hasMore}},{{/hasMore}}{{/allParams}}): ResponseEntity<{{>returnTypes}}> { --- > {{#reactive}}{{^isListContainer}}suspend {{/isListContainer}}{{/reactive}}fun {{operationId}}({{#allParams}}{{>queryParams}}{{>pathParams}}{{>headerParams}}{{>bodyParams}}{{>formParams}},{{/allParams}} httpServletRequest: Httpservletrequest): ResponseEntity<{{>returnTypes}}> { > loggingService.info(httpservletrequest)
ã¡ãã£ã¨åããã¥ããã§ãããLoggingService
ã® injectionã HttpServletRequest
ãå¼æ°ã«è¿½å ã loggingService.info(httpServletRequest)
ãAPIå¦çã®åã«è¿½å ãã¦ãã¾ãã
çæã«ã¯ -t
ã§ãã³ãã¬ã¼ãã®ãã£ã¬ã¯ããªãæå®ãã¾ãã
% openapi-generator generate -g kotlin-spring ... -t mod-kotlin-spring # ãã³ãã¬ã¼ããã£ã¬ã¯ããªã®æå®
ãã³ãã¬ã¼ãã®å¤æ´ãåæ ããã¾ããã
@RestController @Validated @RequestMapping("\${api.base-path:/api/v1}") class TodosApiControllerController(@Autowired val loggingSerice: LoggingService, @Autowired(required = true) val service: TodosApiControllerService) { @RequestMapping( value = ["/todos/{id}"], produces = ["application/json"], method = [RequestMethod.GET]) fun getTodo( @PathVariable("id") id: kotlin.Int, httpServletRequest: Httpservletrequest ): ResponseEntity<ApiTodo> { loggingService.info(httpservletrequest) return ResponseEntity(service.getTodo(id), HttpStatus.valueOf(200)) }
axiosãdateãparseãã¦ãããªã
OpenAPIã§ã¯æ¥ä»ãæ¥æãRFC3339ã«åããã¦full-date
, date-time
ãå®ç¾©ã§ãã¾ãã
kotlin-spring
ã§ã¯ java.time.OffsetDateTime
ã¨ãã¦å®ç¾©ããã¾ã*4ã
package com.m3.todo.adapter.restapi.model import com.fasterxml.jackson.annotation.JsonProperty /** * æå» * @param datetimte æå» */ data class ApiDatetime( @JsonProperty("datetimte") val datetimte: java.time.OffsetDateTime? = null )
typescript-axios
ã§ã Date
ã¨ãã¦å®ç¾©ããã¾ãã
/** * æå» * @export * @interface ApiDatetime */ export interface ApiDatetime { /** * æå» * @type {Date} * @memberof ApiDatetime */ datetimte?: Date; }
ããããå®éã«ãµã¼ãããåãåãã¨ISO8601å½¢å¼ã® string
ã®ã¾ã¾æ¸¡ããã¦ãã¾ãã¾ãã
ãã£ããAPIã³ã¼ããèªåçæããã¦ãéä¿¡ãparseå¦çãä¸åæ°ã«ããªãã¦ãããªã£ãã®ã«ããã¯æããã
ããã§ãaxios
ã® interceptor
ã®æ©è½ã使ã£ã¦ response
ã then
ã catch
ã«æ¸¡ãåã«èªåã®å¦çãæãã§ã
ããã§åä¿¡ããJSONãæ¤æ»ãã¦Dateã«å¤æãããã¨ã«ãã¾ããã
import axios, { AxiosResponse } from "axios"; function isArray(item: any): boolean { return item && typeof item === "object" && item.constructor === Array; } function isObject(item: any): boolean { return item && typeof item === "object" && item.constructor === Object; } function isString(item: any): boolean { return typeof item === "string" || item instanceof String; } const ISO_8601_PATTERN = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d+)?([+-]\d{2}:\d{2}|Z)/; /** * ISO8601 ãã¿ã¼ã³ã®æ¥ä»ãDateãªãã¸ã§ã¯ãã«å¤æãã */ function parseDateValues(item: any) { if (isArray(item)) { return item.map((i: any) => parseDateValues(i)); } if (!isObject(item)) { if (isString(item) && ISO_8601_PATTERN.test(item)) { return new Date(item); } return item; } const newObj = new Object(); Object.keys(item).map(key => { Object.defineProperty(newObj, key, { value: parseDateValues(item[key]), writable: true, enumerable: true, configurable: true }); }); return newObj; } const apiAxios = axios.create(); apiAxios.interceptors.response.use((response: AxiosResponse<any>) => { response.data = parseDateValues(response.data); return response; });
å
¥ã£ã¦ããJSONã®valueã string
ã§ãããã«ISO8601ã®ãã¿ã¼ã³ã«
ãããããã Date
ã«å¤æããå®è£
ã«ãªã£ã¦ãã¾ãã
ãããããããã¹ã¦å¤æãã¦ãã¾ãã®ã§ãkeyåã«æ¡ä»¶ãã¤ãããªã©ãããã«å¶ç´ãå ããæ¹ãå®å
¨ããããã¾ããã
ãã®ã«ã¹ã¿ãã¤ãºãã axios
ã APIã¯ã©ã¤ã¢ã³ãçææã«æ¸¡ãã¨ãinterceptorã§å¤æå¦çãå®è¡ããã¾ãã
let client: TodosApi = new TodosApi({}, "/api/v1", apiAxios);
We are hiring!
åé ã§ç´¹ä»ããããã«ãç¾å¨æ°ãµã¼ãã¹ç«ã¡ä¸ãã®çã£æä¸ã§ãã ä¸ç·ã«éçºã«åå ãã¦ããã仲éãåéä¸ã§ãã ãæ°è»½ã«ãåãåãããã ããã
*1:Typescriptã¯åããã¸ã§ã¯ãã§è«¦ãã¦ããã®ã§ä»åå ¥ãããã£ã https://www.m3tech.blog/entry/2019/04/23/114832
*2:主ã«ããã¯ã¨ã³ãã§APIã®ã¹ãã¼ãã¨business logicã®ã¢ãã«ãæ··ãããªãããã«ãããã
*3:loggingã ã£ããinterceptorã¨ãã§ããã
*4:ãªãã·ã§ã³ã§æå®ããã°å¥ã®classãæå®ã§ãã¾ã https://openapi-generator.tech/docs/usage#examples