Używanie Node.js, Express i Express Router do implementacji bardzo prostego interfejsu API REST
Aby poznać możliwości Node.js i Express, próbuję zaimplementować bardzo prosty interfejs API REST. API mówi, co powinno zrobić, zamiast to robić, więc mogę skupić się na strukturze frameworka. Wymagania dotyczące interfejsu API są następujące:
- Mock API do gry z pytaniami i odpowiedziami.
- / qanda endpoint: repozytorium wszystkich par pytanie-odpowiedź.
- / qanda / questionId endpoint: konkretna para pytanie-odpowiedź.
- Powinien obsługiwać czasowniki HTTP GET, POST, PUT, DELETE.
- Załóżmy, że treść żądania HTTP to JSON.
Wszystko to jest arbitralne. Po prostu próbuję się zawiesić w obsłudze i analizowaniu żądań i odpowiedzi HTTP. Aby utrzymać porządek, używam również modułów Express Router i Node.js.
Oto index.js
'use strict';
const express = require('express');
const qanda = require('./routes/qanda.js');
const port = 9999;
const app = express();
app.use('/qanda', qanda);
app
.all('/', (req, res, nxt) => {
res
.status(200)
.send('Welcome!');
})
.listen(port, () => {
console.log(`Listening to localhost:${port}`);
});
A oto ./routes/qanda.js
'use strict';
const express = require('express');
const qandaRouter = express.Router();
qandaRouter.use(express.json());
qandaRouter
.route('/')
.get((req, res, nxt) => {
res
.status(200)
.send('Sending all the qandas!');
})
.post((req, res, nxt) => {
res
.status(200)
.send(`Adding ${req.body.q} = ${req.body.a}`); }) .put((req, res, nxt) => { res .status(405) .send('PUT not supported.'); }) .delete((req, res, nxt) => { res .status(200) .send('Deleting all qands!'); }); qandaRouter .route('/:qId') .get((req, res, nxt) => { res .status(200) .send(`Sending qanda ${req.params.qId}`);
})
.post((req, res, nxt) => {
res
.status(405)
.send('POST not supported.');
})
.put((req, res, nxt) => {
res
.status(200)
.send(`Updating ${req.params.qId} with ${req.body.q} = ${req.body.a}`); }) .delete((req, res, nxt) => { res .status(200) .send(`Deleting qanda ${req.params.qId}`);
});
module.exports = qandaRouter;
Nie mogę się doczekać wszelkiego rodzaju opinii! Od stylu po najlepsze praktyki, anty-wzorce - cokolwiek przyjdzie Ci do głowy, jest bardzo cenione! Mam kilka konkretnych pytań:
- Czy metoda łańcuchowa jest zwykłym sposobem obsługi czegoś takiego w Expressie?
- Czy styl łączenia metod jest czytelny i możliwy do utrzymania?
- Czy istnieje bardziej zwięzły sposób na osiągnięcie tego?
- Co zrobiłbyś inaczej?
Odpowiedzi
Po przeanalizowaniu napisanych przez ciebie kodów mogłem dostrzec pewne rzeczy, które nie są właściwie zrobione. Poniżej znajduje się kilka rzeczy, które podkreśliłem:
- Struktura bazy kodu nie jest w porządku, dlatego może być możliwa do utrzymania i skalowalna, jeśli baza kodu się rozrośnie.
- Konwencja nazewnictwa RESTful API nie jest akceptowana. Punkty końcowe dowolnego interfejsu API REST powinny zawierać tylko zasoby (rzeczowniki) i używać metod (czasowników) HTTP do wykonywania czynności. Nazwałeś swój punkt końcowy jako / qanda zamiast / qandas . Zalecam przeczytanie tych wytycznych RESTful API Designing autorstwa Mahesha Haldara opublikowanych na Hackernoon. To świetne źródło informacji dla programistów.
- Po trzecie, nie oddzieliłeś swojego serwera od express; oba są połączone razem. Nie jest to najlepsza praktyka.
- Kiedy API jest używane przez inne aplikacje, uaktualnienie API z pewnymi zmianami również doprowadziłoby do zerwania istniejącego kontraktu usługowego - zostało to zauważone przez wiele aplikacji, które świadczą usługi API. Najlepszą praktyką jest zawsze poprzedzać wszystkie adresy URL przedrostkiem / api / v1 / users . Jeśli istnieją jakiekolwiek istotne uaktualnienie łamanie możemy nazwać nowy zestaw API jak V2, V3 lub v1.XX . odpowiednio.
Z własnego doświadczenia podszedłbym do tego w następujący sposób:
- Najpierw utworzę katalog o nazwie qanda-api , w nim utworzę inne katalogi o nazwie src oraz dwa pliki app.js i server.js, które będą znajdować się w katalogu głównym z src
- Po drugie, w src utworzę dwa podkatalogi zwane trasami i kontrolerami
- Po trzecie, zainicjuję rejestr npm w katalogu głównym katalogu projektu, który śledzi wszystkie zainstalowane pakiety npm , wpisując poniższe polecenie za pośrednictwem terminala:
npm init
- Następnie zainstaluję trzy zależności, których będę używać przez ten sam terminal, ale tym razem musisz mieć dostęp do internetu:
npm zainstaluj express dotenv morgan
- Jeśli powyższy proces zostanie wykonany pomyślnie, zobaczysz zainstalowane pakiety w pliku o nazwie package.json
Teraz zróbmy kod, brudząc sobie ręce
- Najpierw utwórzmy plik o nazwie controllers / qandaController.js w innym, aby zapisać naszą logikę qanda, która zostanie wyeksportowana do pliku trasy.
exports.getAllQandas = (req, res) => {
res.status(200).json({
status: 'success',
message: 'Documments retrieved successfully',
data: 'The data goes here from the data!'
});
};
exports.getQanda = (req, res) => {
res.status(200).json({
status: 'success',
message: 'Doc retrieved successfully',
data: 'The data goes here from the data!'
});
};
exports.createQanda = (req, res) => {
res.status(201).json({
status: 'success',
message: 'Qanda created successfully',
data: 'Return the created data here!'
});
};
exports.updateQanda = (req, res) => {
res.status(200).json({
status: 'success',
message: 'Doc updated successfully',
data: 'The updated data goes here!'
});
};
exports.deleteQanda = (req, res) => {
res.status(200).json({
status: 'success',
message: 'Doc deleted!',
data: 'Return the Deleted data'
});
};
- Teraz nadszedł czas, abyśmy stworzyli naszą trasę qanda w plikach Routes / qandaRoutes.js . Następnie zaimportujemy logikę kontrolera do jego obsługi trasy poniżej.
const express = require('express');
/**
* import the qanda controller
*/
const qandaController = require('./../controllers/qandaController');
const router = express.Router();
router
.route('/')
.get(qandaController.getAllQandas)
.post(qandaController.createQanda);
router
.route('/:id')
.get(qandaController.getQanda)
.patch(qandaController.updateQanda)
.delete(qandaController.deleteQanda);
module.exports = router;
- Otwórzmy plik, który utworzyliśmy wcześniej, o nazwie app.js w innym, aby skorzystać z zainstalowanego ekspresu. Zawartość tego pliku jest przeznaczona tylko do ekspresu, który jest następnie eksportowany na jego serwer:
const express = require('express');
const morgan = require('morgan');
const qandaRouter = require('./routes/qandaRoutes');
const app = express();
if (process.env.NODE_ENV === 'development') app.use(morgan('dev'));
// ROUTES
app.use('/api/v1/qandas', qandaRouter);
/*
** HANDLING UNHANDLED ROUTES
*/
app.all('*', (req, res, next) => {
res.status(404).json({
status: 'fail',
message: `Can't find ${req.originalUrl} on this Server!`
});
});
module.exports = app;
- Stwórzmy nasz serwer, który stanie się naszym punktem wejścia do aplikacji. W tym miejscu następuje faktyczne wykonanie, w którym wyeksportowany plik app.js jest używany przez wstawienie go jako argumentu w funkcji createServer ().
require('dotenv').config({ path: '.env' });
const http = require('http');
const app = require('./app');
const port = process.env.PORT || 5000;
const server = http.createServer(app);
server.listen(port, () => console.log(`App running on port ${port}`));
Wniosek
Zdecydowanie zaleca się, aby podczas pisania kodów zawsze rozdzielać obawy i mieć czystą bazę kodu, którą można utrzymać i skalować w czasie. Jeśli chodzi o projektowanie API, organizacja kodu to coś więcej, a jedną z nich jest zasada DRY (Don't Repeat Yourself). Jeśli chcesz dowiedzieć się więcej o projektowaniu API w Node.JS, polecam ten kurs Jonasa . Demistyfikuje abstrakcyjne koncepcje i uczył najlepszych praktyk. Zawarłem tę odpowiedź w projektach API w Github: API i Tourism API . Możesz klonować lub modyfikować według potrzeb.