


From Zero to Storefront: My Journey Building a Car Rental Website and Mobile App
Contents
- Introduction
- Tech Stack
- Quick Overview
- API
- Frontend
- Mobile App
- Admin Dashboard
- Points of Interest
- Resources
Source code: https://github.com/aelassas/bookcars
Demo: https://bookcars.dynv6.net:3002
Introduction
The idea emerged from a desire to build without boundaries – a fully customizable car rental website and mobile app where every aspect is within your control:
- Own the UI/UX: Design unique customer experiences without fighting against template limitations
- Control the Backend: Implement custom business logic and data structures that perfectly match the requirements
- Master DevOps: Deploy, scale, and monitor the application with preferred tools and workflows
- Extend Freely: Add new features and integrations without platform constraints or additional fees
Tech Stack
Here's the tech stack that made it possible:
- TypeScript
- Node.js
- MongoDB
- React
- MUI
- React Native
- Expo
- Stripe
- Docker
A key design decision was made to use TypeScript due to its numerous advantages. TypeScript offers strong typing, tooling, and integration, resulting in high-quality, scalable, more readable and maintainable code that is easy to debug and test.
I chose React for its powerful rendering capabilities, MongoDB for flexible data modeling, and Stripe for secure payment processing.
By choosing this stack, you're not just building a website and mobile app – you're investing in a foundation that can evolve with your needs, backed by robust open-source technologies and a growing developer community.
React stands out as an excellent choice due to its:
- Component-Based Architecture
- Lets you break down complex UIs into smaller, reusable pieces
- Makes code more maintainable and easier to test
- Enables better code organization and reusability
- Virtual DOM Performance
- React's virtual DOM efficiently updates only what's necessary
- Results in faster page loads and better user experience
- Reduces unnecessary re-renders
- Rich Ecosystem
- Vast library of pre-built components
- Extensive tooling
- Large community for support and resources
- Strong Developer Experience
- Hot reloading for immediate feedback
- Excellent debugging tools
- JSX makes writing UI code more intuitive
- Industry Support
- Backed by Meta (formerly Facebook)
- Used by many major companies
- Continuous development and improvements
- Flexibility
- Works well for both small and large applications
- Can be integrated gradually into existing projects
- Supports multiple rendering strategies (client-side, server-side, static)
Quick overview
In this section, you'll see the main pages of the frontend, the admin dashboard and the mobile app.
Frontend
From the frontend, the user can search for available cars, choose a car and checkout.
Below is the main page of the frontend where the user can choose pickup and drop-off points and time, and search for available cars.
Below is the search result of the main page where the user can choose a car for rental.
Below is the checkout page where the user can set rental options and checkout. If the user is not registered, he can checkout and register at the same time. He will receive a confirmation and activation email to set his password if he is not registered yet.
Below is the sign in page. On production, authentication cookies are httpOnly, signed, secure and strict sameSite. These options prevent XSS, CSRF and MITM attacks. Authentication cookies are protected against XST attacks as well via a custom middleware.
Below is the sign up page.
Below is the page where the user can see and manage his bookings.
Below is the page where the user can see a booking in detail.
Below is the contact page.
Below is the car rental locations page.
Below is the page where the customer can see and manage his notifications.
There are other pages but these are the main pages of the frontend.
Admin Dashboard
There are three types of users:
- Admin: He has full access on the admin dashboard. He can do everything.
- Supplier: He has restricted access on the admin dashboard. He can only manage his cars and bookings.
- User: He has only access to the frontend and the mobile app. He cannot access the admin dashboard.
The platform is designed to work with multiple suppliers. Each supplier can manage his car fleet and bookings from the admin dashboard. The platform can also work with only one supplier as well.
From the admin dashboard, the admin user can create and manage suppliers, cars, locations, users and bookings.
When the admin user creates a new supplier, the supplier will receive an automatic email for creating his account to access the admin dashboard so he can manage his car fleet and bookings.
Below is the sign in page of the admin dashboard.
Below is the dashboard page of the admin dashboard where admins and suppliers can see and manage bookings.
Below is the page where car fleet is displayed and can be managed.
Below is the page where admins and suppliers can create new cars by providing an image and car info. For car options to be included for free, set 0 for the corresponding car option. Otherwise, set the price of the option or leave it empty if you don't want to include it.
Below is the page where admins and suppliers can edit cars.
Below is the page where admins can manage platform users.
Below is the page where to edit bookings.
There are other pages but these are the main pages of the admin dashboard.
API
The api exposes all functions needed for the admin dashboard, the frontend and the mobile app. The API follow the MVC design pattern. JWT is used for authentication. There are some functions that need authentication such as functions related to managing cars, bookings and customers, and others that do not need authentication such as retrieving locations and available cars for non authenticated users:
- ./api/src/models/ folder contains MongoDB models.
- ./api/src/routes/ folder contains Express routes.
- ./api/src/controllers/ folder contains controllers.
- ./api/src/middlewares/ folder contains middlewares.
- ./api/src/config/env.config.ts contains the configuration and TypeScript type definitions.
- ./api/src/lang/ folder contains localization.
- ./api/src/app.ts is the main server where routes are loaded.
- ./api/index.ts is the main entry point of the api.
index.ts is the main entry point of the api:
import 'dotenv/config' import process from 'node:process' import fs from 'node:fs/promises' import http from 'node:http' import https, { ServerOptions } from 'node:https' import * as env from './config/env.config' import * as databaseHelper from './common/databaseHelper' import app from './app' import * as logger from './common/logger' if ( await databaseHelper.connect(env.DB_URI, env.DB_SSL, env.DB_DEBUG) && await databaseHelper.initialize() ) { let server: http.Server | https.Server if (env.HTTPS) { https.globalAgent.maxSockets = Number.POSITIVE_INFINITY const privateKey = await fs.readFile(env.PRIVATE_KEY, 'utf8') const certificate = await fs.readFile(env.CERTIFICATE, 'utf8') const credentials: ServerOptions = { key: privateKey, cert: certificate } server = https.createServer(credentials, app) server.listen(env.PORT, () => { logger.info('HTTPS server is running on Port', env.PORT) }) } else { server = app.listen(env.PORT, () => { logger.info('HTTP server is running on Port', env.PORT) }) } const close = () => { logger.info('Gracefully stopping...') server.close(async () => { logger.info(`HTTP${env.HTTPS ? 'S' : ''} server closed`) await databaseHelper.close(true) logger.info('MongoDB connection closed') process.exit(0) }) } ['SIGINT', 'SIGTERM', 'SIGQUIT'].forEach((signal) => process.on(signal, close)) }
This is a TypeScript file that starts a server using Node.js and Express. It imports several modules including dotenv, process, fs, http, https, mongoose, and app. It then checks if the HTTPS environment variable is set to true, and if so, creates an HTTPS server using the https module and the provided private key and certificate. Otherwise, it creates an HTTP server using the http module. The server listens on the port specified in the PORT environment variable.
The close function is defined to gracefully stop the server when a termination signal is received. It closes the server and the MongoDB connection, and then exits the process with a status code of 0. Finally, it registers the close function to be called when the process receives a SIGINT, SIGTERM, or SIGQUIT signal.
app.ts is the main entry point of the api:
import express from 'express' import compression from 'compression' import helmet from 'helmet' import nocache from 'nocache' import cookieParser from 'cookie-parser' import i18n from './lang/i18n' import * as env from './config/env.config' import cors from './middlewares/cors' import allowedMethods from './middlewares/allowedMethods' import supplierRoutes from './routes/supplierRoutes' import bookingRoutes from './routes/bookingRoutes' import locationRoutes from './routes/locationRoutes' import notificationRoutes from './routes/notificationRoutes' import carRoutes from './routes/carRoutes' import userRoutes from './routes/userRoutes' import stripeRoutes from './routes/stripeRoutes' import countryRoutes from './routes/countryRoutes' import * as helper from './common/helper' const app = express() app.use(helmet.contentSecurityPolicy()) app.use(helmet.dnsPrefetchControl()) app.use(helmet.crossOriginEmbedderPolicy()) app.use(helmet.frameguard()) app.use(helmet.hidePoweredBy()) app.use(helmet.hsts()) app.use(helmet.ieNoOpen()) app.use(helmet.noSniff()) app.use(helmet.permittedCrossDomainPolicies()) app.use(helmet.referrerPolicy()) app.use(helmet.xssFilter()) app.use(helmet.originAgentCluster()) app.use(helmet.crossOriginResourcePolicy({ policy: 'cross-origin' })) app.use(helmet.crossOriginOpenerPolicy()) app.use(nocache()) app.use(compression({ threshold: 0 })) app.use(express.urlencoded({ limit: '50mb', extended: true })) app.use(express.json({ limit: '50mb' })) app.use(cors()) app.options('*', cors()) app.use(cookieParser(env.COOKIE_SECRET)) app.use(allowedMethods) app.use('/', supplierRoutes) app.use('/', bookingRoutes) app.use('/', locationRoutes) app.use('/', notificationRoutes) app.use('/', carRoutes) app.use('/', userRoutes) app.use('/', stripeRoutes) app.use('/', countryRoutes) i18n.locale = env.DEFAULT_LANGUAGE await helper.mkdir(env.CDN_USERS) await helper.mkdir(env.CDN_TEMP_USERS) await helper.mkdir(env.CDN_CARS) await helper.mkdir(env.CDN_TEMP_CARS) await helper.mkdir(env.CDN_LOCATIONS) await helper.mkdir(env.CDN_TEMP_LOCATIONS) export default app
First of all, we retrieve MongoDB connection string, then we establish a connection with the MongoDB database. Then we create an Express app and load middlewares such as cors, compression, helmet, and nocache. We set up various security measures using the helmet middleware library. we also import various route files for different parts of the application such as supplierRoutes, bookingRoutes, locationRoutes, notificationRoutes, carRoutes, and userRoutes. Finally, we load Express routes and export app.
There are 8 routes in the api. Each route has its own controller following the MVC design pattern and SOLID principles. Below are the main routes:
- userRoutes: Provides REST functions related to users
- supplierRoutes: Provides REST functions related to suppliers
- countryRoutes: Provides REST functions related to countries
- locationRoutes: Provides REST functions related to locations
- carRoutes: Provides REST functions related to cars
- bookingRoutes: Provides REST functions related to bookings
- notificationRoutes: Provides REST functions related to notifications
- stripeRoutes: Provides REST functions related to Stripe payment gateway
We are not going to explain each route one by one. We'll take, for example, countryRoutes and see how it was made:
import express from 'express' import routeNames from '../config/countryRoutes.config' import authJwt from '../middlewares/authJwt' import * as countryController from '../controllers/countryController' const routes = express.Router() routes.route(routeNames.validate).post(authJwt.verifyToken, countryController.validate) routes.route(routeNames.create).post(authJwt.verifyToken, countryController.create) routes.route(routeNames.update).put(authJwt.verifyToken, countryController.update) routes.route(routeNames.delete).delete(authJwt.verifyToken, countryController.deleteCountry) routes.route(routeNames.getCountry).get(authJwt.verifyToken, countryController.getCountry) routes.route(routeNames.getCountries).get(authJwt.verifyToken, countryController.getCountries) routes.route(routeNames.getCountriesWithLocations).get(countryController.getCountriesWithLocations) routes.route(routeNames.checkCountry).get(authJwt.verifyToken, countryController.checkCountry) routes.route(routeNames.getCountryId).get(authJwt.verifyToken, countryController.getCountryId) export default routes
First of all, we create an Express Router. Then, we create the routes using their name, method, middlewares and controllers.
routeNames contains countryRoutes route names:
const routes = { validate: '/api/validate-country', create: '/api/create-country', update: '/api/update-country/:id', delete: '/api/delete-country/:id', getCountry: '/api/country/:id/:language', getCountries: '/api/countries/:page/:size/:language', getCountriesWithLocations: '/api/countries-with-locations/:language/:imageRequired/:minLocations', checkCountry: '/api/check-country/:id', getCountryId: '/api/country-id/:name/:language', } export default routes
countryController contains the main business logic regarding countries. We are not going to see all the source code of the controller since it's quite large but we'll take create controller function for example.
Below is Country model:
import { Schema, model } from 'mongoose' import * as env from '../config/env.config' const countrySchema = new Schema<env.country>( { values: { type: [Schema.Types.ObjectId], ref: 'LocationValue', required: [true, "can't be blank"], validate: (value: any): boolean => Array.isArray(value), }, }, { timestamps: true, strict: true, collection: 'Country', }, ) const Country = model<env.country>('Country', countrySchema) export default Country </env.country></env.country>
Below is env.Country TypeScript type:
export interface Country extends Document { values: Types.ObjectId[] name?: string }
A Country has multiple values. One per language. By default, English and French languages are supported.
Below is LocationValue model:
import 'dotenv/config' import process from 'node:process' import fs from 'node:fs/promises' import http from 'node:http' import https, { ServerOptions } from 'node:https' import * as env from './config/env.config' import * as databaseHelper from './common/databaseHelper' import app from './app' import * as logger from './common/logger' if ( await databaseHelper.connect(env.DB_URI, env.DB_SSL, env.DB_DEBUG) && await databaseHelper.initialize() ) { let server: http.Server | https.Server if (env.HTTPS) { https.globalAgent.maxSockets = Number.POSITIVE_INFINITY const privateKey = await fs.readFile(env.PRIVATE_KEY, 'utf8') const certificate = await fs.readFile(env.CERTIFICATE, 'utf8') const credentials: ServerOptions = { key: privateKey, cert: certificate } server = https.createServer(credentials, app) server.listen(env.PORT, () => { logger.info('HTTPS server is running on Port', env.PORT) }) } else { server = app.listen(env.PORT, () => { logger.info('HTTP server is running on Port', env.PORT) }) } const close = () => { logger.info('Gracefully stopping...') server.close(async () => { logger.info(`HTTP${env.HTTPS ? 'S' : ''} server closed`) await databaseHelper.close(true) logger.info('MongoDB connection closed') process.exit(0) }) } ['SIGINT', 'SIGTERM', 'SIGQUIT'].forEach((signal) => process.on(signal, close)) }
Below is env.LocationValue TypeScript type:
import express from 'express' import compression from 'compression' import helmet from 'helmet' import nocache from 'nocache' import cookieParser from 'cookie-parser' import i18n from './lang/i18n' import * as env from './config/env.config' import cors from './middlewares/cors' import allowedMethods from './middlewares/allowedMethods' import supplierRoutes from './routes/supplierRoutes' import bookingRoutes from './routes/bookingRoutes' import locationRoutes from './routes/locationRoutes' import notificationRoutes from './routes/notificationRoutes' import carRoutes from './routes/carRoutes' import userRoutes from './routes/userRoutes' import stripeRoutes from './routes/stripeRoutes' import countryRoutes from './routes/countryRoutes' import * as helper from './common/helper' const app = express() app.use(helmet.contentSecurityPolicy()) app.use(helmet.dnsPrefetchControl()) app.use(helmet.crossOriginEmbedderPolicy()) app.use(helmet.frameguard()) app.use(helmet.hidePoweredBy()) app.use(helmet.hsts()) app.use(helmet.ieNoOpen()) app.use(helmet.noSniff()) app.use(helmet.permittedCrossDomainPolicies()) app.use(helmet.referrerPolicy()) app.use(helmet.xssFilter()) app.use(helmet.originAgentCluster()) app.use(helmet.crossOriginResourcePolicy({ policy: 'cross-origin' })) app.use(helmet.crossOriginOpenerPolicy()) app.use(nocache()) app.use(compression({ threshold: 0 })) app.use(express.urlencoded({ limit: '50mb', extended: true })) app.use(express.json({ limit: '50mb' })) app.use(cors()) app.options('*', cors()) app.use(cookieParser(env.COOKIE_SECRET)) app.use(allowedMethods) app.use('/', supplierRoutes) app.use('/', bookingRoutes) app.use('/', locationRoutes) app.use('/', notificationRoutes) app.use('/', carRoutes) app.use('/', userRoutes) app.use('/', stripeRoutes) app.use('/', countryRoutes) i18n.locale = env.DEFAULT_LANGUAGE await helper.mkdir(env.CDN_USERS) await helper.mkdir(env.CDN_TEMP_USERS) await helper.mkdir(env.CDN_CARS) await helper.mkdir(env.CDN_TEMP_CARS) await helper.mkdir(env.CDN_LOCATIONS) await helper.mkdir(env.CDN_TEMP_LOCATIONS) export default app
A LocationValue has a language code (ISO 639-1) and a string value.
Below is create controller function:
import express from 'express' import routeNames from '../config/countryRoutes.config' import authJwt from '../middlewares/authJwt' import * as countryController from '../controllers/countryController' const routes = express.Router() routes.route(routeNames.validate).post(authJwt.verifyToken, countryController.validate) routes.route(routeNames.create).post(authJwt.verifyToken, countryController.create) routes.route(routeNames.update).put(authJwt.verifyToken, countryController.update) routes.route(routeNames.delete).delete(authJwt.verifyToken, countryController.deleteCountry) routes.route(routeNames.getCountry).get(authJwt.verifyToken, countryController.getCountry) routes.route(routeNames.getCountries).get(authJwt.verifyToken, countryController.getCountries) routes.route(routeNames.getCountriesWithLocations).get(countryController.getCountriesWithLocations) routes.route(routeNames.checkCountry).get(authJwt.verifyToken, countryController.checkCountry) routes.route(routeNames.getCountryId).get(authJwt.verifyToken, countryController.getCountryId) export default routes
In this function, we retrieve the body of the request, we iterate through the values provided in the body (one value per language) and we create a LocationValue. Finally, we create the country depending on the created location values.
Frontend
The frontend is a web application built with Node.js, React, MUI and TypeScript. From the frontend, the customer can search for available cars depending on pickup and drop-off points and time, choose a car and proceed to checkout:
- ./frontend/src/assets/ folder contains CSS and images.
- ./frontend/src/pages/ folder contains React pages.
- ./frontend/src/components/ folder contains React components.
- ./frontend/src/services/ contains api client services.
- ./frontend/src/App.tsx is the main React App that contains routes.
- ./frontend/src/index.tsx is the main entry point of the frontend.
TypeScript type definitions are defined in the package ./packages/bookcars-types.
App.tsx is the main react App:
const routes = { validate: '/api/validate-country', create: '/api/create-country', update: '/api/update-country/:id', delete: '/api/delete-country/:id', getCountry: '/api/country/:id/:language', getCountries: '/api/countries/:page/:size/:language', getCountriesWithLocations: '/api/countries-with-locations/:language/:imageRequired/:minLocations', checkCountry: '/api/check-country/:id', getCountryId: '/api/country-id/:name/:language', } export default routes
We are using React lazy loading to load each route.
We are not going to cover each page of the frontend, but you can open the source code and see each one if you want.
Mobile App
The platform provides a native mobile app for Android and iOS. The mobile app is built with React Native, Expo and TypeScript. Like for the frontend, the mobile app allows the customer to search for available cars depending on pickup and drop-off points and time, choose a car and proceed to checkout.
The customer receives push notifications if his booking is updated from the backend. Push notifications are built with Node.js, Expo Server SDK and Firebase.
- ./mobile/assets/ folder contains images.
- ./mobile/screens/ folder contains main React Native screens.
- ./mobile/components/ folder contains React Native components.
- ./mobile/services/ contains api client services.
- ./mobile/App.tsx is the main React Native App.
TypeScript type definitions are defined in:
- ./mobile/types/index.d.ts
- ./mobile/types/env.d.ts
- ./mobile/miscellaneous/bookcarsTypes.ts
./mobile/types/ is loaded in ./mobile/tsconfig.json as follow:
import { Schema, model } from 'mongoose' import * as env from '../config/env.config' const countrySchema = new Schema<env.country>( { values: { type: [Schema.Types.ObjectId], ref: 'LocationValue', required: [true, "can't be blank"], validate: (value: any): boolean => Array.isArray(value), }, }, { timestamps: true, strict: true, collection: 'Country', }, ) const Country = model<env.country>('Country', countrySchema) export default Country </env.country></env.country>
App.tsx is the main entry point of the React Native app:
import 'react-native-gesture-handler' import React, { useCallback, useEffect, useRef, useState } from 'react' import { RootSiblingParent } from 'react-native-root-siblings' import { NavigationContainer, NavigationContainerRef } from '@react-navigation/native' import { StatusBar as ExpoStatusBar } from 'expo-status-bar' import { SafeAreaProvider } from 'react-native-safe-area-context' import { Provider } from 'react-native-paper' import * as SplashScreen from 'expo-splash-screen' import * as Notifications from 'expo-notifications' import { StripeProvider } from '@stripe/stripe-react-native' import DrawerNavigator from './components/DrawerNavigator' import * as helper from './common/helper' import * as NotificationService from './services/NotificationService' import * as UserService from './services/UserService' import { GlobalProvider } from './context/GlobalContext' import * as env from './config/env.config' Notifications.setNotificationHandler({ handleNotification: async () => ({ shouldShowAlert: true, shouldPlaySound: true, shouldSetBadge: true, }), }) // // Prevent native splash screen from autohiding before App component declaration // SplashScreen.preventAutoHideAsync() .then((result) => console.log(`SplashScreen.preventAutoHideAsync() succeeded: ${result}`)) .catch(console.warn) // it's good to explicitly catch and inspect any error const App = () => { const [appIsReady, setAppIsReady] = useState(false) const responseListener = useRef<notifications.subscription>() const navigationRef = useRef<navigationcontainerref>>(null) useEffect(() => { const register = async () => { const loggedIn = await UserService.loggedIn() if (loggedIn) { const currentUser = await UserService.getCurrentUser() if (currentUser?._id) { await helper.registerPushToken(currentUser._id) } else { helper.error() } } } // // Register push notifiations token // register() // // This listener is fired whenever a user taps on or interacts with a notification (works when app is foregrounded, backgrounded, or killed) // responseListener.current = Notifications.addNotificationResponseReceivedListener(async (response) => { try { if (navigationRef.current) { const { data } = response.notification.request.content if (data.booking) { if (data.user && data.notification) { await NotificationService.markAsRead(data.user, [data.notification]) } navigationRef.current.navigate('Booking', { id: data.booking }) } else { navigationRef.current.navigate('Notifications', {}) } } } catch (err) { helper.error(err, false) } }) return () => { Notifications.removeNotificationSubscription(responseListener.current!) } }, []) setTimeout(() => { setAppIsReady(true) }, 500) const onReady = useCallback(async () => { if (appIsReady) { // // This tells the splash screen to hide immediately! If we call this after // `setAppIsReady`, then we may see a blank screen while the app is // loading its initial state and rendering its first pixels. So instead, // we hide the splash screen once we know the root view has already // performed layout. // await SplashScreen.hideAsync() } }, [appIsReady]) if (!appIsReady) { return null } return ( <globalprovider> <safeareaprovider> <provider> <stripeprovider publishablekey="{env.STRIPE_PUBLISHABLE_KEY}" merchantidentifier="{env.STRIPE_MERCHANT_IDENTIFIER}"> <rootsiblingparent> <navigationcontainer ref="{navigationRef}" onready="{onReady}"> <expostatusbar> <p>We are not going to cover each screen of the mobile app, but you can open the source code and see each one if you want.</p> <h2> Admin Dashboard </h2> <p>The admin dashboard is a web application built with Node.js, React, MUI and TypeScript. From the backend, admins can create and manage suppliers, cars, locations, customers and bookings. When new suppliers are created from the backend, they will receive an email prompting them to create an account in order to access the backend and manage their car fleet and bookings.</p> <ul> <li>./backend/assets/ folder contains CSS and images.</li> <li>./backend/pages/ folder contains React pages.</li> <li>./backend/components/ folder contains React components.</li> <li>./backend/services/ contains api client services.</li> <li>./backend/App.tsx is the main React App that contains routes.</li> <li>./backend/index.tsx is the main entry point of the admin dashboard.</li> </ul> <p>TypeScript type definitions are defined in the package ./packages/bookcars-types.</p> <p>App.tsx of the backend follow similar logic like App.tsx of the frontend.</p> <p>We are not going to cover each page of the admin dashboard but you can open the source code and see each one if you want.</p> <h2> Points of Interest </h2> <p>Building the mobile app with React Native and Expo is very easy. Expo makes mobile development with React Native very simple.</p> <p>Using the same language (TypeScript) for backend, frontend and mobile development is very convenient.</p> <p>TypeScript is a very interesting language and has many advantages. By adding static typing to JavaScript, we can avoid many bugs and produce high quality, scalable, more readable and maintainable code that is easy to debug and test.</p> <p>That's it! I hope you enjoyed reading this article.</p> <h2> Resources </h2> <ol> <li>Overview</li> <li>Architecture</li> <li>Installing (Self-hosted)</li> <li>Installing (VPS)</li> <li> Installing (Docker) <ol> <li>Docker Image</li> <li>SSL</li> </ol> </li> <li>Setup Stripe</li> <li>Build Mobile App</li> <li> Demo Database <ol> <li>Windows, Linux and macOS</li> <li>Docker</li> </ol> </li> <li>Run from Source</li> <li> Run Mobile App <ol> <li>Prerequisites</li> <li>Instructions</li> <li>Push Notifications</li> </ol> </li> <li>Change Currency</li> <li>Add New Language</li> <li>Unit Tests and Coverage</li> <li>Logs</li> </ol> </expostatusbar></navigationcontainer></rootsiblingparent></stripeprovider></provider></safeareaprovider></globalprovider></navigationcontainerref></notifications.subscription>
The above is the detailed content of From Zero to Storefront: My Journey Building a Car Rental Website and Mobile App. For more information, please follow other related articles on the PHP Chinese website!

Different JavaScript engines have different effects when parsing and executing JavaScript code, because the implementation principles and optimization strategies of each engine differ. 1. Lexical analysis: convert source code into lexical unit. 2. Grammar analysis: Generate an abstract syntax tree. 3. Optimization and compilation: Generate machine code through the JIT compiler. 4. Execute: Run the machine code. V8 engine optimizes through instant compilation and hidden class, SpiderMonkey uses a type inference system, resulting in different performance performance on the same code.

JavaScript's applications in the real world include server-side programming, mobile application development and Internet of Things control: 1. Server-side programming is realized through Node.js, suitable for high concurrent request processing. 2. Mobile application development is carried out through ReactNative and supports cross-platform deployment. 3. Used for IoT device control through Johnny-Five library, suitable for hardware interaction.

I built a functional multi-tenant SaaS application (an EdTech app) with your everyday tech tool and you can do the same. First, what’s a multi-tenant SaaS application? Multi-tenant SaaS applications let you serve multiple customers from a sing

This article demonstrates frontend integration with a backend secured by Permit, building a functional EdTech SaaS application using Next.js. The frontend fetches user permissions to control UI visibility and ensures API requests adhere to role-base

JavaScript is the core language of modern web development and is widely used for its diversity and flexibility. 1) Front-end development: build dynamic web pages and single-page applications through DOM operations and modern frameworks (such as React, Vue.js, Angular). 2) Server-side development: Node.js uses a non-blocking I/O model to handle high concurrency and real-time applications. 3) Mobile and desktop application development: cross-platform development is realized through ReactNative and Electron to improve development efficiency.

The latest trends in JavaScript include the rise of TypeScript, the popularity of modern frameworks and libraries, and the application of WebAssembly. Future prospects cover more powerful type systems, the development of server-side JavaScript, the expansion of artificial intelligence and machine learning, and the potential of IoT and edge computing.

JavaScript is the cornerstone of modern web development, and its main functions include event-driven programming, dynamic content generation and asynchronous programming. 1) Event-driven programming allows web pages to change dynamically according to user operations. 2) Dynamic content generation allows page content to be adjusted according to conditions. 3) Asynchronous programming ensures that the user interface is not blocked. JavaScript is widely used in web interaction, single-page application and server-side development, greatly improving the flexibility of user experience and cross-platform development.

Python is more suitable for data science and machine learning, while JavaScript is more suitable for front-end and full-stack development. 1. Python is known for its concise syntax and rich library ecosystem, and is suitable for data analysis and web development. 2. JavaScript is the core of front-end development. Node.js supports server-side programming and is suitable for full-stack development.


Hot AI Tools

Undresser.AI Undress
AI-powered app for creating realistic nude photos

AI Clothes Remover
Online AI tool for removing clothes from photos.

Undress AI Tool
Undress images for free

Clothoff.io
AI clothes remover

AI Hentai Generator
Generate AI Hentai for free.

Hot Article

Hot Tools

SecLists
SecLists is the ultimate security tester's companion. It is a collection of various types of lists that are frequently used during security assessments, all in one place. SecLists helps make security testing more efficient and productive by conveniently providing all the lists a security tester might need. List types include usernames, passwords, URLs, fuzzing payloads, sensitive data patterns, web shells, and more. The tester can simply pull this repository onto a new test machine and he will have access to every type of list he needs.

PhpStorm Mac version
The latest (2018.2.1) professional PHP integrated development tool

SAP NetWeaver Server Adapter for Eclipse
Integrate Eclipse with SAP NetWeaver application server.

DVWA
Damn Vulnerable Web App (DVWA) is a PHP/MySQL web application that is very vulnerable. Its main goals are to be an aid for security professionals to test their skills and tools in a legal environment, to help web developers better understand the process of securing web applications, and to help teachers/students teach/learn in a classroom environment Web application security. The goal of DVWA is to practice some of the most common web vulnerabilities through a simple and straightforward interface, with varying degrees of difficulty. Please note that this software

SublimeText3 Mac version
God-level code editing software (SublimeText3)