We will build an Express API server container on Docker and build an environment that returns a simple response.
Up to the previous article, we created an application container / DB container, but this time we will build an API server container that connects them and actually pass the data to the front.
Since the server side has a lot of volume, the articles on the basic construction of Express and the introduction of Sequelize are separated.
How to build [TypeScript + Vue + Express + MySQL] environment with Docker ~ Vue edition ~ How to build [TypeScript + Vue + Express + MySQL] environment with Docker ~ MySQL edition ~
docker-compose.yml
version: "3"
services:
app:
container_name: app_container
build: ./docker/app
ports:
- 8080:8080
volumes:
- ./app:/app
stdin_open: true
tty: true
environment:
TZ: Asia/Tokyo
command: yarn serve
#Add from here
api:
container_name: api_container
build: ./docker/api
ports:
- 3000:3000
volumes:
- ./api:/api
tty: true
environment:
CHOKIDAR_USEPOLLING: 1
TZ: Asia/Tokyo
depends_on:
- db
#Add up to here
db:
container_name: db_container
build: ./docker/db
image: mysql:5.7
ports:
- 3306:3306
volumes:
- ./db/conf/my.cnf:/etc/mysql/conf.d/mysql.cnf
- ./db/init_db:/docker-entrypoint-initdb.d
- test_data:/var/lib/mysql
environment:
- MYSQL_DATABASE=${MYSQL_DATABASE}
- MYSQL_USER=${MYSQL_USER}
- MYSQL_PASSWORD=${MYSQL_PASSWORD}
- MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD}
- TZ="Asia/Tokyo"
volumes:
test_data:
docker/api/Dockerfile
FROM node:12.13-alpine
ENV NODE_PATH /usr/local/lib/node_modules
WORKDIR /api
$ docker-compose up -d
$ docker ps
OK if api_container is started!
$ docker exec -it api_container sh
$ yarn init
$ yarn add --dev typescript ts-node prettier @types/node
.prettierrc
{
"singleQuote": true,
"semi": false
}
tsconfig init
$ ./node_modules/.bin/tsc --init
Since tsconfig.json is generated, we will build the application based on this.
tsconfig.json
"include": ["./**/*"] //Add to the end
package.json
"scripts": {
"ts-node": "ts-node index.ts"
},
Set the script so that the TypeScript file can be executed on ts-node.
api/index.ts
const message: string = 'test'
console.log(message)
Create a main file to execute TypeScript. Enter a dummy to check if ts-node works properly.
test
$ yarn ts-node
If TypeScript is compiled and the above message is displayed, it's OK!
We will introduce a library that will automatically build when there is a change in the file.
$ yarn add --dev nodemon
nodemon.json
{
"watch": ["api/**/*"],
"ext": "ts",
"exec": "ts-node ./index.ts"
}
Create a nodemon configuration file. Execute ts-node via nodemon and set everything directly under the api directory to be monitored.
package.json
"scripts": {
"tsc": "tsc",
"ts-node": "ts-node index.ts",
"nodemon": "nodemon"
},
docker-compose.yml
api:
container_name: api_container
build: ./docker/api
ports:
- 3000:3000
volumes:
- ./api:/api
tty: true
environment:
CHOKIDAR_USEPOLLING: 1
TZ: Asia/Tokyo
depends_on:
- db
command: yarn nodemon #Add this guy
Add a command to automatically start up via nodemon when the container starts.
This completes the TypeScript execution and automatic build settings when changing. From here, we will build Express, which will be the actual API server.
$ yarn add express
$ yarn add --dev @types/express
api/index.ts
import express from 'express'
const app = express()
const port = 3000
app.get('/', (req, res) => res.send('Test Express!'))
app.listen(port, () => console.log(`Example app listening on port ${port}!`))
$ exit //Get out of the container
$ docker-compose stop //Stop container
$ docker-compose up -d //Container restart
If you access localhost: 3000 and "Test Express!" Is displayed, the response is returned without any problem! Just in case, change another message in "Test Express!" In index.ts and check if the message changes when you access localhost: 3000 again. If it changes automatically, nodemon is working properly and automatic build is done!
Currently, it can only be accessed on localhost: 3000, but I want to call the API according to the URL as shown below, so I will set the routing.
Example)
api/routes/index.ts
import Express from 'express'
const router = Express.Router()
export default router
api/routes/tests/testsController.ts
import { Router } from 'express'
const router = Router()
export default router
This is the end of the pre-processing, and we will actually specify the routing. This time, I changed it so that it can be accessed with "localhost: 3000 / api / tests"
api/routes/index.ts
import Express from 'express'
const router = Express.Router()
router.get('/', (req, res, next) => {
res.send('test')
})
export default router
api/index.ts
import express from 'express'
import router from './routes/index'
const app = express()
const port = 3000
app.use('/api', router)
app.listen(port, () => console.log(`Example app listening on port ${port}!`))
[App.use ('/ api', router)] set above With this setting, when localhost: 3000 / api is passed, it will move to the routing of routes / index.ts. Let's actually check the operation.
Access with localhost: 3000 / api If ** test ** is displayed, it's OK!
In an actual application, it is necessary to branch the endpoint when accessing multiple DBs, so set the routing so that it branches as shown below.
localhost: 3000 / api / ** {branch according to this value} **
api/routes/tests/testsController.ts
import { Router } from 'express'
const router = Router()
router.get('/', (req, res, next) => {
res.send('Routing complete!')
})
export default router
api/routes/index.ts
import Express from 'express'
import tests from './tests/testsController'
const router = Express.Router()
router.use('/tests', tests)
export default router
Access localhost: 3000 / api / tests If "Routing completed!" Is displayed, it's OK!
This completes the routing settings!
Next, create a handler that switches between a pattern that returns a normal response and a response when an error occurs.
api/constants/error.ts
export type ErrorCode = {
status: number
type: string
message: string
}
/**
*Error if the parameter is incorrect
*/
export const PARAMETER_INVALID: ErrorCode = {
status: 400,
type: 'PARAMETER_INVALID',
message: 'The parameter is invalid.',
}
/**
*Error when data does not exist
*/
export const NO_DATA_EXISTS: ErrorCode = {
status: 400,
type: 'NO_DATA_EXISTS',
message: 'No data exists.',
}
When the number of error patterns increases, we will increase the error code here.
api/core/handler.ts
import { Request, Response } from 'express'
import { ErrorCode } from '../constants/error'
/**
*Function to handle API
*/
export class Handler {
constructor(private req: Request, private res: Response) {}
/**
*Send data
*/
json<T>(data: T): void {
this.res.json({ data: data })
}
/**
*Send error
*/
error(error: ErrorCode): void {
this.res.status(error.status).send({ error: error })
}
}
If an error occurs in the API, it branches to an error output, and if it is normal, it returns in json format.
Create an API template that retrieves all data from the tests table when accessed with localhost: 3000 / api / test.
api/routes/tests/get_tests.ts
import { Request, Response } from 'express'
import { Handler } from '../../core/handler'
export class GetTests {
handler: Handler
constructor(req: Request, res: Response) {
this.handler = new Handler(req, res)
}
/**
*Main processing
*/
async main() {
const data = this.getTests()
return this.handler.json(data)
}
/**
*Returns a message
*/
getTests() {
const message = 'get_tests run'
return message
}
}
This is the API that is actually called
api/routes/tests/testsController.ts
import { Router } from 'express'
import { GetTests } from '../tests/get_tests' //Import the above API
const router = Router()
router.get('/', (req, res, next) => {
new GetTests(req, res).main().catch(next) //Change
})
export default router
localhost:3000/api/tests
It's OK if it returns in JSON format like this!
Thank you for your hard work! At this point, you have an application container, a DB container, and an API server container. From the next time, I will connect each container and actually pass the DB data to the front and draw it!
How to build [TypeScript + Vue + Express + MySQL] environment with Docker ~ Vue edition ~ How to build [TypeScript + Vue + Express + MySQL] environment with Docker ~ MySQL edition ~ How to build [TypeScript + Vue + Express + MySQL] environment with Docker ~ Express edition ~ How to build [TypeScript + Vue + Express + MySQL] environment with Docker ~ Sequelize ~
Recommended Posts