Tempat kerja pertama atau cara mulai mengembangkan API di Node.js

Pendahuluan


Dalam artikel ini saya ingin berbagi emosi dan keterampilan yang saya peroleh dalam mengembangkan REST API pertama di Node.js menggunakan TypeScript, seperti yang mereka katakan, dari awal. Ceritanya cukup dangkal: โ€œSaya lulus dari universitas, menerima diploma. Ke mana harus pergi bekerja? " Seperti yang mungkin Anda tebak, masalahnya tidak terhindar, meskipun saya tidak perlu berpikir terlalu banyak. Pengembang (lulusan dari spesialisasi yang sama) meminta magang. Saya percaya bahwa ini adalah praktik yang cukup umum dan ada banyak kisah serupa. Tanpa berpikir dua kali, saya memutuskan untuk mencoba tangan saya dan pergi ...

gambar

Hari pertama Memperkenalkan Node.js


Saya datang ke pengembangan back-end. Perusahaan IT ini menggunakan platform Node.js , yang sama sekali tidak saya kenal. Saya berlari sedikit ke depan, lupa memberi tahu pembaca bahwa saya belum pernah mengembangkan apa pun dalam JavaScript (kecuali untuk beberapa skrip dengan kode salin). Saya memahami algoritma pekerjaan dan arsitektur aplikasi web secara keseluruhan, karena saya mengembangkan CRUD di Java, Python dan Clojure, tetapi ini tidak cukup. Oleh karena itu, hari pertama saya benar-benar mengabdikan diri untuk mempelajari Node.js, screencast ini sangat membantu.

Saat mempelajari kerangka kerja Express , manajer paket npm , serta file seperti package.json dan tsconfig.json, kepala saya hanya melihat-lihat jumlah informasi. Pelajaran lain adalah bahwa menguasai semua materi pada saat yang sama dekat dengan tugas yang mustahil. Pada akhirnya, saya masih dapat mengkonfigurasi lingkungan dan dapat menjalankan server web ekspres! Tetapi masih terlalu dini untuk bersukacita, karena dia pulang dengan penuh kesalahpahaman. Perasaan bahwa saya tenggelam di dunia JS yang luas tidak meninggalkan saya selama satu menit, jadi saya perlu reboot.

Hari kedua Memperkenalkan TypeScript


Reboot yang sama diikuti pada hari itu juga. Pada titik ini, saya sepenuhnya menyadari masalah saya, kami akan beralih sedikit lebih rendah. Mengetahui bahwa itu tidak perlu untuk menulis dalam JavaScipt murni, pelatihan dari Node.js mengalir dengan lancar ke bahasa TypeScript, yaitu, fitur dan sintaksisnya. Di sini saya melihat tipe yang lama ditunggu-tunggu, yang tanpanya pemrograman secara harfiah 2 hari yang lalu, tidak dalam bahasa pemrograman fungsional. Ini adalah kesalahpahaman terbesar saya, yang mencegah saya untuk memahami dan mempelajari kode yang ditulis dalam JavaScript pada hari pertama.

Dia sebelumnya menulis sebagian besar dalam bahasa pemrograman berorientasi objek seperti Java, C ++, C #. Menyadari kemungkinan-kemungkinan dari TypeScript, saya merasa nyaman. Bahasa pemrograman ini benar-benar memberi saya kehidupan di lingkungan yang kompleks ini, seperti yang tampak bagi saya pada waktu itu. Menjelang akhir hari saya benar-benar mengatur lingkungan, meluncurkan server (sudah ada di TypeScript), menghubungkan perpustakaan yang diperlukan, yang akan saya bahas di bawah ini. Intinya: siap untuk mengembangkan API. Kami lolos langsung ke pengembangan ...

Pengembangan API


Penjelasan tentang prinsip kerja dan penjelasan lain tentang apa REST API itu, akan kami tinggalkan, karena forum memiliki banyak artikel tentang ini dengan contoh dan pengembangan dalam berbagai bahasa pemrograman.
gambar

Tugasnya adalah sebagai berikut:

Buat layanan dengan REST API. Otorisasi oleh token pembawa (/ info, / latency, / logout). CORS yang dikonfigurasi untuk akses dari domain apa pun. DB - MongoDB. Buat token di setiap panggilan.

Deskripsi API:

  1. / masuk [POST] - meminta token bearer dengan id dan kata sandi // menerima data dalam json
  2. / daftar [POST] - pendaftaran pengguna baru: // menerima data dalam json
  3. / info [GET] - mengembalikan id pengguna dan jenis id, memerlukan token yang dikeluarkan oleh pembawa dalam otentikasi
  4. / latency [GET] - mengembalikan penundaan (ping), membutuhkan token yang dikeluarkan oleh pembawa dalam otentikasi
  5. / logout [GET] - dengan parameter all: true - menghapus semua token pembawa pengguna atau false - hanya menghapus token pembawa saat ini

Saya perhatikan segera, tugasnya terlihat sangat sederhana untuk pengembang aplikasi web. Tetapi tugas itu harus diimplementasikan dalam bahasa pemrograman, yang 3 hari lalu tidak tahu apa-apa! Bahkan bagi saya, itu terlihat sangat transparan di atas kertas dan dalam Python implementasi membutuhkan waktu sedikit, tetapi saya tidak punya opsi seperti itu. Tumpukan pengembangan menyebabkan masalah.

Sarana implementasi


Jadi, saya menyebutkan bahwa pada hari kedua saya sudah mempelajari beberapa perpustakaan (kerangka kerja), kita akan mulai dari ini. Untuk perutean, saya memilih perute-pengontrol , dipandu oleh banyak kesamaan dengan dekorator dari Spring Framework (Java). Sebagai ORM, saya memilih typeorm , meskipun bekerja dengan MongoDB dalam mode eksperimental, itu cukup untuk tugas seperti itu. Saya menggunakan uuid untuk menghasilkan token, variabel dimuat menggunakan dotenv .

Startup server web


Biasanya, express digunakan dalam bentuk murni, tetapi saya menyebutkan kerangka Pengendali Routing, yang memungkinkan kita untuk membuat server express sebagai berikut:

//  Express const app = createExpressServer({ // routePrefix: process.env.SERVER_PREFIX, //  defaults: { nullResultCode: Number(process.env.ERROR_NULL_RESULT_CODE), undefinedResultCode: Number(process.env.ERROR_NULL_UNDEFINED_RESULT_CODE), paramOptions: { required: true } }, //   authorizationChecker: authorizationChecker, // controllers: [UserController] }); //  app.listen(process.env.SERVER_PORT, () => { console.log(process.env.SERVER_MASSAGE); }); 


Seperti yang Anda lihat, tidak ada yang rumit. Bahkan, kerangka kerja memiliki lebih banyak fitur, tetapi tidak perlu untuk mereka.
  • routePrefix hanyalah awalan di url Anda setelah alamat server, misalnya: localhost : 3000 / awalan
  • default - tidak ada yang menarik, hanya menginisialisasi kode kesalahan
  • otorisasiChecker - peluang bagus untuk kerangka kerja untuk memeriksa otorisasi pengguna, maka kami akan mempertimbangkan secara lebih rinci
  • pengendali adalah salah satu bidang utama tempat kami menentukan pengontrol yang digunakan dalam aplikasi kami


Koneksi DB


Sebelumnya, kami telah meluncurkan server web, jadi kami akan terus terhubung ke database MongoDB, setelah sebelumnya menyebarkannya di server lokal. Instalasi dan konfigurasi dijelaskan secara rinci dalam dokumentasi resmi . Kami akan langsung mempertimbangkan koneksi menggunakan typeorm:

 //  createConnection({ type: 'mongodb', host: process.env.DB_HOST, database: process.env.DB_NAME_DATABASE, entities: [ User ], synchronize: true, logging: false }).catch(error => console.log(error)); 


Semuanya cukup sederhana, Anda perlu menentukan beberapa parameter:

  • ketik - DB
  • host - alamat ip tempat Anda menggunakan basis data
  • database - nama database yang sebelumnya dibuat di mongodb
  • sinkronisasi - sinkronisasi otomatis dengan database (Catatan: sulit untuk menguasai migrasi pada waktu itu)
  • entitas - di sini kami menunjukkan entitas yang melakukan sinkronisasi


Sekarang kita menghubungkan mulai server dan koneksi ke DB. Saya perhatikan bahwa impor sumber daya berbeda dari yang klasik yang digunakan di Node.js. Sebagai hasilnya, kami mendapatkan file yang dapat dieksekusi berikut, dalam kasus saya main.ts:

 import 'reflect-metadata'; import * as dotenv from 'dotenv'; import { createExpressServer } from 'routing-controllers'; import { createConnection } from 'typeorm'; import { authorizationChecker } from './auth/authorizationChecker'; import { UserController } from './controllers/UserController'; import { User } from './models/User'; dotenv.config(); //  createConnection({ type: 'mongodb', host: process.env.DB_HOST, database: process.env.DB_NAME_DATABASE, entities: [ User ], synchronize: true, logging: false }).catch(error => console.log(error)); //  Express const app = createExpressServer({ // routePrefix: process.env.SERVER_PREFIX, //  defaults: { nullResultCode: Number(process.env.ERROR_NULL_RESULT_CODE), undefinedResultCode: Number(process.env.ERROR_NULL_UNDEFINED_RESULT_CODE), paramOptions: { required: true } }, //   authorizationChecker: authorizationChecker, // controllers: [UserController] }); //  app.listen(process.env.SERVER_PORT, () => { console.log(process.env.SERVER_MASSAGE); }); 

Entitas


Biarkan saya mengingatkan Anda bahwa tugasnya adalah untuk mengotentikasi dan mengotorisasi pengguna, masing-masing, kami membutuhkan entitas: Pengguna. Tapi itu tidak semua, karena setiap pengguna memiliki token dan bukan satu! Oleh karena itu, perlu untuk membuat entitas Token.

Pengguna

 import { ObjectID } from 'bson'; import { IsEmail, MinLength } from 'class-validator'; import { Column, Entity, ObjectIdColumn } from 'typeorm'; import { Token } from './Token'; //  @Entity() export class User { //  @ObjectIdColumn() id: ObjectID; //Email    @Column() @IsEmail() email: string; //  @Column({ length: 100 }) @MinLength(2) password: string; //  @Column() token: Token; } 

Di tabel Pengguna, kami membuat bidang - array token yang sangat untuk pengguna. Kami juga mengaktifkan validator calss , karena pengguna harus masuk melalui email.

Token

 import { Column, Entity } from 'typeorm'; //   @Entity() export class Token { @Column() accessToken: string; @Column() refreshToken: string; @Column() timeKill: number; } 

Basis adalah sebagai berikut:

gambar

Otorisasi Pengguna


Untuk otorisasi, kami menggunakan otorisasiChecker (salah satu parameter saat membuat server, lihat di atas), untuk kenyamanan, kami meletakkannya di file terpisah:

 import { Action, UnauthorizedError } from 'routing-controllers'; import { getMongoRepository } from 'typeorm'; import { User } from '../models/User'; export async function authorizationChecker(action: Action): Promise<boolean> { let token: string; if (action.request.headers.authorization) { //   token = action.request.headers.authorization.split(" ", 2); const repository = getMongoRepository(User); const allUsers = await repository.find(); for (let i = 0; i < allUsers.length; i++) { if (allUsers[i].token.accessToken.toString() === token[1]) { return true; } } } else { throw new UnauthorizedError('This user has not token.'); } return false; } 

Setelah otentikasi, setiap pengguna memiliki token sendiri, sehingga kami dapat memperoleh token yang diperlukan dari header respons, terlihat seperti ini: Bearer 046a5f60-c55e-11e9-af71-c75526de439e . Sekarang kita dapat memeriksa apakah token ini ada, setelah fungsi mengembalikan informasi otorisasi: true - pengguna diotorisasi, salah - pengguna tidak diotorisasi. Dalam aplikasi ini, kita dapat menggunakan dekorator yang sangat nyaman di controller: @Authorized (). Pada titik ini, fungsi otorisasiChecker akan dipanggil, yang akan mengembalikan respons.

Logika


Untuk mulai dengan, saya ingin menjelaskan logika bisnis, karena controller adalah satu baris pemanggilan metode di bawah kelas yang disajikan. Juga, di controller kami akan menerima semua data, dalam kasus kami itu akan menjadi JSON dan Query. Kami akan mempertimbangkan metode untuk tugas-tugas individual, dan pada akhirnya kami akan membentuk file terakhir, yang disebut UserService.ts. Saya perhatikan bahwa pada saat itu tidak ada cukup pengetahuan untuk menghilangkan ketergantungan. Jika Anda belum memenuhi istilah dependensi injeksi, saya sangat merekomendasikan untuk membacanya. Saat ini saya menggunakan kerangka kerja DI, yaitu, saya menggunakan wadah, yaitu injeksi melalui konstruktor. Di sini, saya pikir, adalah artikel yang bagus untuk ditinjau. Kami kembali ke tugas.

  • / signin [POST] - otentikasi dari pengguna terdaftar. Semuanya sangat sederhana dan transparan. Kami hanya perlu menemukan pengguna ini di database dan mengeluarkan token baru. Untuk membaca dan menulis, MongoRepository digunakan.

     async userSignin(user: User): Promise<string> { // Mongo repository const repo = getMongoRepository(User); //       let userEmail = await repo.findOne({ email: user.email, password: user.password }); if (userEmail) { //  userEmail = await this.setToken(userEmail); //    repo.save(userEmail); return userEmail.token.accessToken; } return process.env.USER_SERVICE_RESPONSE; } 
  • / daftar [POST] - daftarkan pengguna baru. Metode yang sangat mirip, karena pada awalnya kami juga mencari pengguna sehingga kami tidak memiliki pengguna terdaftar dengan satu email. Selanjutnya, kami menulis pengguna baru ke database, setelah mengeluarkan token.

     async userSignup(newUser: User): Promise<string> { // Mongo repository const repo = getMongoRepository(User); //   email (   2    email) const userRepeat = await repo.findOne({ email: newUser.email }); if (!userRepeat) { //  newUser = await this.setToken(newUser); //   const addUser = getMongoManager(); await addUser.save(newUser); return newUser.token.accessToken; } else { return process.env.USER_SERVICE_RESPONSE; } } 
  • / info [GET] - mengembalikan id pengguna dan jenis id, memerlukan token yang dikeluarkan oleh pembawa dalam otentikasi. Gambar juga transparan: pertama kita mendapatkan token pengguna saat ini dari header permintaan, kemudian mencarinya di database dan menentukan kepada siapa itu terletak, dan mengembalikan pengguna yang ditemukan.

     async getUserInfo(req: express.Request): Promise<User> { // Mongo repository const repository = getMongoRepository(User); //    const user = await this.findUser(req, repository); return user; } private async findUser(req: express.Request, repository: MongoRepository<User>): Promise<User> { if (req.get(process.env.HEADER_AUTH)) { //  const token = req.get(process.env.HEADER_AUTH).split(' ', 2); //    const usersAll = await repository.find(); //  for (let i = 0; i < usersAll.length; i++) { if (usersAll[i].token.accessToken.toString() === token[1]) { return usersAll[i]; } } } } 

  • / latency [GET] - mengembalikan penundaan (ping), membutuhkan token yang dikeluarkan oleh pembawa dalam otentikasi. Paragraf artikel yang sama sekali tidak menarik. Di sini saya hanya menggunakan perpustakaan yang sudah jadi untuk memeriksa penundaan tcp-ping.

     getLatency(): Promise<IPingResult> { function update(progress: number, total: number): void { console.log(progress, '/', total); } const latency = ping({ address: process.env.PING_ADRESS, attempts: Number(process.env.PING_ATTEMPTS), port: Number(process.env.PING_PORT), timeout: Number(process.env.PING_TIMEOUT) }, update).then(result => { console.log('ping result:', result); return result; }); return latency; } 
  • / logout [GET] - dengan parameter all: true - menghapus semua token pembawa pengguna atau false - hanya menghapus token pembawa saat ini. Kami hanya perlu menemukan pengguna, memeriksa parameter kueri dan menghapus token. Saya pikir semuanya harus jelas.

     async userLogout(all: boolean, req: express.Request): Promise<void> { // Mongo repository const repository = getMongoRepository(User); //    const user = await this.findUser(req, repository); if (all) { // true    user.token.accessToken = process.env.GET_LOGOUT_TOKEN; user.token.refreshToken = process.env.GET_LOGOUT_TOKEN; //  repository.save(user); } else { // false    user.token.accessToken = process.env.GET_LOGOUT_TOKEN; //  repository.save(user); } } 


Pengendali


Banyak yang tidak perlu menjelaskan apa yang dibutuhkan dan bagaimana controller digunakan dalam pola MVC, tapi saya masih akan mengatakan dua kata. Singkatnya, pengontrol adalah tautan antara pengguna dan aplikasi yang mengalihkan data di antara mereka. Logikanya sepenuhnya dijelaskan di atas, metode yang dipanggil sesuai dengan rute, terdiri dari URI dan server ip (contoh: localhost: 3000 / masuk) . Saya sebutkan sebelumnya tentang dekorator di controller: Dapatkan , POST , @Authorized dan yang paling penting adalah @JsonController. Fitur lain yang sangat penting dari kerangka kerja ini adalah bahwa jika kita ingin mengirim dan menerima JSON, maka kita menggunakan dekorator ini daripada Pengontrol .

 import * as express from 'express'; import { Authorized, Body, Get, Header, JsonController, NotFoundError, Post, QueryParam, Req, UnauthorizedError } from 'routing-controllers'; import { IPingResult } from '@network-utils/tcp-ping'; import { User } from '../models/User'; import { UserService } from '../services/UserService'; //    JSON @JsonController() export class UserController { userService: UserService //  constructor() { this.userService = new UserService(); } //  @Post('/signin') async login(@Body() user: User): Promise<string> { const responseSignin = await this.userService.userSignin(user); if (responseSignin !== process.env.USER_SERVICE_RESPONSE) { return responseSignin; } else { throw new NotFoundError(process.env.POST_SIGNIN_MASSAGE); } } //  @Post('/signup') async registrateUser(@Body() newUser: User): Promise<string> { const responseSignup = await this.userService.userSignup(newUser); if (responseSignup !== process.env.USER_SERVICE_RESPONSE) { return responseSignup; } else { throw new UnauthorizedError(process.env.POST_SIGNUP_MASSAGE); } } //   @Get('/info') @Authorized() async getId(@Req() req: express.Request): Promise<User> { return this.userService.getUserInfo(req); } //   @Authorized() @Get('/latency') getPing(): Promise<IPingResult> { return this.userService.getLatency(); } @Get('/logout') async deleteToken(@QueryParam("all") all: boolean, @Req() req: express.Request): Promise<void> { this.userService.userLogout(all, req); } } 

Kesimpulan


Dalam artikel ini, saya ingin mencerminkan tidak lagi komponen teknis dari kode yang benar atau sesuatu seperti itu, tetapi hanya untuk berbagi fakta bahwa seseorang dapat membangun aplikasi web menggunakan database dan mengandung setidaknya beberapa logika dari nol absolut dalam lima hari. Coba pikirkan, tidak ada instrumen yang familier, ingat diri Anda atau letakkan saja di tempat saya. Dalam kasus ini tidak ada kasus yang mengatakan: "Saya yang terbaik, Anda tidak akan pernah bisa melakukan itu." Sebaliknya, ini adalah seruan dari jiwa seseorang yang saat ini benar-benar senang dengan dunia Node.js dan berbagi ini dengan Anda. Dan kenyataan bahwa tidak ada yang mustahil, Anda hanya perlu mengambil dan melakukan!

Tentu saja, tidak dapat dipungkiri bahwa penulis tidak tahu apa-apa dan duduk untuk menulis kode untuk pertama kalinya. Tidak, pengetahuan tentang OOP, prinsip-prinsip API REST, ORM, dan basis data ada dalam jumlah yang cukup. Dan ini hanya bisa mengatakan bahwa cara untuk mencapai hasil sama sekali tidak memainkan peran apa pun dan berkata dalam gaya: "Saya tidak akan pergi ke pekerjaan ini, ada bahasa pemrograman yang saya tidak pelajari", bagi saya sekarang itu hanya manifestasi seseorang bukan dari kelemahan, melainkan perlindungan dari lingkungan eksternal yang tidak dikenal. Tapi apa yang ada untuk disembunyikan, rasa takut itu hadir dengan saya.

Untuk meringkas. Saya ingin menasihati siswa dan orang-orang yang belum memulai karir mereka di bidang TI, tidak perlu takut akan alat pengembangan dan teknologi yang tidak dikenal. Kawan-kawan senior pasti akan membantu Anda (jika Anda beruntung juga saya), mereka akan menjelaskan secara terperinci dan menjawab pertanyaan, karena masing-masing dari mereka ada di posisi ini. Tetapi jangan lupa bahwa keinginan Anda adalah aspek yang paling penting!

Tautan ke proyek

Source: https://habr.com/ru/post/id466745/


All Articles