Welcome to part two of the Full-stack TypeScript with Ionic and NestJS series!

In part one of the series, we went over the benefits of using TypeScript on both the client and server, shared an introduction to NestJS, and built a simple app, that’s already up and running, called GoSpaceRanger.

So far in GoSpaceRanger, we have a simple list screen showing a list of missions our space rangers can go on:

In this second part of our series, we are going to dive deeper into building out the Nest backend service, including adding a database to store missions and using some Nest features to keep our code clean and reusable.

Getting Back Into the Project

If you followed along in part one, go back into the main project folder, open up the gsr-client (the Ionic project) and gsr-server (the Nest project) folders.

If you would like to start fresh in part two, clone the project repo from This email address is being protected from spambots. You need JavaScript enabled to view it.:ionic-team/demo-ionic-nest-tutorial.git, and go to the part2-start branch, which leaves you at the same place as if you had completed part one.

Open up each of the projects in your favorite code editor and run ionic serve in the gsr-client folder to kick off the Ionic development server, and npm run start:dev in the gsr-server folder to start the Nest server.

Friends of Nest

To help build out the backend, we will look into a few additional libraries to use within the Nest service.

The following libraries are NPM packages that provide additional functionality in the Nest backend. Like Nest, they are written in TypeScript and take advantage of TypeScript features—like decorators and type meta-data—to provide a cleaner, more declarative coding experience.

TypeORM

TypeORM is an ORM (object relational mapper), which helps manage and interact with databases. TypeORM uses a code-first methodology, which means you create your classes, and TypeORM automatically generates the tables and columns in your database for you. TypeORM is heavily influenced by other enterprise-grade ORMs, such as Entity Framework, and provides a performant, scalable, and easy to use data access library.

TypeORM supports the most common relational databases: such as MySql, PostgreSQL, SQLite, MS SQL, and more. Setting up your database instance is outside the scope of this tutorial, so please reference the database vendor’s documentation if you need assistance there. In this tutorial, I am going to use PostgreSQL, but feel free to stick with whatever database you have available—the only part that should change with the code is the connection info to the database.

To install TypeORM (and a couple of additional helper libraries) run the following command in the gsr-server folder:

npm install @nestjs/typeorm typeorm pg

Note: pg is the PostgreSQL driver for Node. If you use a different database, install the appropriate driver.

Class Transformer

Class Transformer is a powerful library that helps with the serialization from everyday JavaScript objects into classes, as well as classes back to plain JavaScript. This library helps convert data coming in from requests to the proper class type.

Install class-transformer via npm in the gsr-server folder:

npm install class-transformer

Adding a Database

First, we need to register the TypeORM library with Nest and provide it with the connection information to our database.

In the server’s app module, add TypeOrmModule to the list of imports like so:

gsr-server/src/app.module.ts:

imports: [
    TypeOrmModule.forRoot({
      type: 'postgres',
      host: 'localhost',
      port: 5432,
      username: 'mydbuser', // Update to your db username
      password: 'mydbpassword', // Update to your db password
      database: 'gospaceranger',
      entities: [__dirname + '/**/*.entity{.ts,.js}'],
      synchronize: true,
    })
  ],

Import TypeOrmModule from ‘@nestjs/typeorm’

Note: The synchronize property keeps your database schema up to date with your models, which helps during development, but make sure to disable this in production.

Additionally, you’ll want to make sure to update the connection details (username, password, database, etc.) to your actual setup. The database that you will connect to needs to be a blank database, TypeORM will automatically create the schema for you based on the model definitions.

Now that TypeORM is set up, we need a database entity to represent our missions. We already have the mission model, so let’s use that.

Open up Mission and replace what is currently defined for the model with the following:

gsr-server/src/models/mission.model.ts:

import { Entity, PrimaryGeneratedColumn, Column } from 'typeorm';

@Entity()
export class Mission {
  @PrimaryGeneratedColumn()
  id?: number;

  @Column({ length: 50 })
  title: string;

  @Column()
  reward: number;

  @Column()
  active: boolean;

  @Column() createdAt: Date = new Date();
  @Column() createdBy: string = 'user';
  @Column() isDeleted: boolean = false;
}

Here our model has decorators applied to the class and its properties imported from TypeORM.

At the class level, the @Entity decorators inform TypeORM that this class represents an entity in the database. TypeORM will create a table based on this model and associated all data for this model in that table. By default, it names the table based on the model’s name, but you could override that by passing in a string to @Entity.

The @PrimaryGeneratedColumn decorator specifies that our id property will be the primary key for the missions table and that the value of it will be generated by the database.

The rest of the properties have @Column decorators, which instructs TypeORM to created columns for each of these fields. The data type for the column will be derived from the properties TypeScript type.

There are three additional fields on this mission model that wasn’t on the original. The createdAt, and createdBy fields store when the record was created and by whom. The isDeleted field gives us soft delete support, in which when a user deletes a record we mark it as deleted, but we keep the original around for our records.

These additional meta-data fields only belong to the database entity, and shouldn’t be returned to the front end app. We will add the logic in the MissionsService to not returned deleted missions when fetched.

When the Nest server restarts, TypeORM analyzes all the entities and constructs the database schema on it. You should see a schema similar to the following now in your database:

Before we try to pull back any data, we will add a mission manually using SQL. Run this SQL statement in your database’s query tool to insert a single mission:

INSERT INTO public.mission(
    title, reward, active, "createdAt", "createdBy", "isDeleted")
    VALUES ('Rescue cat stuck in asteroid', 500, true, '4/20/2019', 'user', false);

Retrieve Missions from the Database

TypeORM uses the Repository pattern, in which all your data interaction for a particular entity is handled through an object called a repository. The repository has methods to retrieve, create, update, and delete data for that particular entity. TypeORM provides these repositories for us, and we need to register them with Nest.

Go back into the server AppModule, and append TypeOrmModule.forFeature([Mission]) to the list of imports:

gsr-server/src/app.module.ts:

imports: [
    TypeOrmModule.forRoot(...),
    TypeOrmModule.forFeature([Mission])
]

This import registers the Mission entity with both TypeORM and Nest and provides us a repository that we can now inject.

Open up MissionsService, and update the class to the following:

gsr-server/src/missions/missions.service.ts:

export class MissionsService {
  constructor(
    @InjectRepository(Mission) private missionsRepository: Repository<Mission>,
  ) {}

  async getMissions(): Promise<Mission[]> {
    return this.missionsRepository.find({isDeleted: false});
  }
}

Import InjectRepository from ‘@nestjs/typeorm’ and Repository from ‘typeorm’.

We inject a Mission repository in the constructor of the class using TypeORM’s generic Repository class. If you are not familiar with generics, they are a TypeScript language feature that allows you to use a base class but have it work with a variety of different types, without having to write that class for each of those types. In our case, we have a base Repository class, but by passing in the Mission as a generic parameter (via Repository<Mission>, we set up this repository to work with the Mission type.

The getMissions method was updated to return data from the repository instead of the hard-coded array that we also removed. In the method, the repository’s find method is called, and we specify that we only want to return missions that are not deleted.

With the service updated, we should now be able to retrieve all the missions stored in the database, and since the interface of the API hasn’t changed, the Ionic client should now show live data.

However, if you take a look at the HTTP request, you will see that the data being returned has the meta-data fields (createdAt, createdBy, and isDeleted) returned as well. Ideally, we would want to exclude these properties as the client does not need them.

We utilize the class-transformer library we introduced above to do just that. The library has an @Exclude decorator, which when ran through class-transformer, will exclude any members decorated with it.

Add the @Exclude decorator to those members from the Mission model:

gsr-server/src/models/mission.model.ts:

@Exclude() @Column() createdAt: Date = new Date();
@Exclude() @Column() createdBy: string = 'user';
@Exclude() @Column() isDeleted: boolean = false;

To exclude the properties, we run them through the classToPlain function from the class-transformer library. We could do this in the controller like so:

@Get()
async getMissions() {
  const missionEntities = await this.missionsService.getMissions();
  const missions = classToPlain(missionEntities);
  return missions;
}

However, this adds some cruft to our controller methods. We would have to repeat this code everywhere we return a mission, and repeating code violates the DRY (don’t repeat yourself) principle.

Fortunately, Nest provides a mechanism called interceptors that allow manipulation of data before being returned in the request. Let’s take a look at building one next.

Nest Interceptors

Interceptors are a piece of Nest middleware that gets access to both the start of the request (before the controller), and then again before the response is sent out (after the controller is done). Interceptors are an ideal fit for any data manipulation that needs to be done before a request is sent out.

In our case, we will build an interceptor to call the plainToClass function from class-transformer on the data the controller returns, that way any transformations we set up on the models get applied.

Run the following command in the gsr-server folder to create a new interceptor using the Nest CLI:

nest g interceptor util/data

Update the DataInterceptor class with the following:

gsr-server/src/util/data.interceptor.ts:

import { classToPlain } from 'class-transformer';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';
import { CallHandler, ExecutionContext, Injectable, NestInterceptor, } from '@nestjs/common';

@Injectable()
export class DataInterceptor implements NestInterceptor {
  intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
    return next.handle().pipe(
      map(data => {
        return classToPlain(data);
      }),
    );
  }
}

The next.handle() method is an observable stream that you can interact with like any other observable. Here, we use the map operator from RXJS to transform the output from one object to another. Specifically, we are using classToPlain to apply any transformations added to our models (like we did with Exclude in Mission).

To use the DataInterceptor, Nest provides a few different options to bind it to a request. We can use the @UseInterceptors decorator and either put it on a class if we want it to apply to the entire controller like so:

@UseInterceptors(DataInterceptor)
@Controller('missions')
export class MissionsController { ... }

Alternatively, we can be more selective and put it only on the route handlers we want:

@UseInterceptors(DataInterceptor)
@Get()
async getMissions() { ... }

A third option allows us to apply the interceptor to run globally by specifying it in the app module:

gsr-server/src/app.module.ts:

@Module({
  providers: [
    {
      provide: APP_INTERCEPTOR,
      useClass: DataInterceptor,
    },
  ],
})
export class AppModule {}

Import APP_INTERCEPTOR from ‘@nestjs/core’ and DataInterceptor from ‘./util/data.interceptor’.

This method is the one we will we use, so go ahead and add this to the list of providers in AppModule.

Once saved, hit the /missions endpoint again and you should see the list of missions minus the createdAt, createdBy, and isDeleted members.

Retrieve a Single Mission

We are now returning a list of missions, so next, let us see how we can specify the id of the mission in the URL to return a particular mission and display the mission in the app.

Starting in the MissionService, the call to get the mission from the repository is fairly straight forward:

gsr-server/src/missions/missions.service.ts:

async getMission(id: number): Promise<Mission | undefined> {
  return this.missionsRepository.findOne(id, {
    where: {
      isDeleted: false,
    },
  });
}

The findOne method on the repository takes the id of the object being looked for. If found, it returns the object, and if not, returns undefined. We also make sure not to return a mission if it is marked as deleted.

Update the MissionController to call into this new service method:

gsr-server/src/missions/missions.controller.ts:

@Get(':id')
async getMission(@Param('id') id: number) {
  return this.missionsService.getMission(id);
}

The Get() decorator here takes in a route parameter named id, and in the getMission method, we extract that parameter out using the new @Param decorator, which in turn assigns the value to id.

If you request https://localhost:3000/missions/1, you should notice that nothing comes back. Why? Parameters are passed in as strings, but we expect the id to be a number, and the repo does not find the mission because of it. In the params to getMission, id is of type number, but, unfortunately, we are practically lying to TypeScript that the value is coming in as a number.

We could parse the string value of id into a number manually inside the controller method before calling into the service, but once again this muddies our controller code with duplicated logic throughout. It would be nice to have a facility to do this for us automatically.

Nest comes to the rescue again!

Nest Pipes

Fortunately, we can use another facet of Nest, called Pipes, to manipulate data that comes in from the request before it reaches the controller. Pipes are another type of Nest middleware, but unlike interceptors, they can transform the data before they reach the controller.

Because we are using TypeScript, Pipes know all the little details about your controller methods, including all the parameters, and the types of those parameters. We can take advantage of this information and use class-transformer library again to convert a plain object into something of a specific type. Let’s see how to do that.

Use the Nest CLI in the gsr-server folder to generate a new pipe:

nest g pipe util/data

Then open up the DataPipe and update its transform method to:

gsr-server/src/util/data.pipe.ts:

transform(value: any, metadata: ArgumentMetadata) {
  const { metatype } = metadata;
  if (!metatype) {
    return value;
  }
  const convertedValue = plainToClass(metatype, value);
  return convertedValue;
}

Import plainToClass from ‘class-transformer’.

The transform method takes two arguments. The first is the original value of the parameter, and the second one is all the meta-data provided by TypeScript about the parameter.

The plainToClass method can use this type information to convert the value into the particular type. In our use case, it takes the type of the id parameter, a number, and then converts the string parameter that gets passed in from the URL.

Bind the DataPipe in the AppModule providers like so:

gsr-server/src/util/data.pipe.ts:

{
  provide: APP_PIPE,
  useClass: DataPipe,
}

Import APP_PIPE from ‘@nestjs/core’ and DataPipe from ‘./util/data.pipe’.

Now, parameters passed into all controller handlers have their types converted automatically. Make a call to https://localhost:3000/missions/1, and you should see a single mission come back.

Wrapping Up

In part two of this series, we spent the majority of time replacing the hard-coded Missions array we had in the MissionsService with a real database backend. We used a library called TypeORM to manage our database access. TypeORM is an excellent companion library to Nest; they are both built with TypeScript and offer the same declarative style programming.

Then, we explored Interceptors and Pipes, a couple of the Nest building blocks that allow you to extract common logic into reusable components and apply them in various ways to your Nest controllers.

In the next post, we will finish building out the rest of GoSpaceRanger, including creating, updating, and deleting missions.

Happy coding! 🚀

Read more

“I’m so happy because today 4.3 is out”

We got a 🔥release today with fantastic updates to the Toast component as well as some much-desired bug fixes. Let’s dig in.

Lithium is the lightest metal and has many uses, like lubrication, medication, pyrotechnics, and electronics. The metal is so soft that it can be cut with a knife. Lithium does not typically occur in nature but comes from other Ionic compounds 🤓.

New Features

There are so many great updates to the Toast component we wanted to dub this release “Lithium Toast.”

Toast Buttons

Toasts show subtle messages to the user, usually in response to feedback or as notifications, and typically go away on their own after a short time. Previously, however, you couldn’t do a whole lot with toasts but display some info and let the user dismiss it. In 4.3, you can now set buttons to add additional functionality to toasts.

The buttons can show text or an icon, have a side property that determines if they show at the start or end of the toast, and a handler that takes a function for custom logic.

Here is the sample code for the above:

async showToast() {
  const toast = await this.toastController.create({
    header: 'API Error',
    message: 'Error saving data.',
    buttons: [
      { icon: 'close', side: 'start' },
      {
        text: 'Retry?',
        side: 'end',
        handler: () => {
          toast.message = 'Retrying...';
          // remove the retry button
          toast.buttons.splice(1, 1);
          // simulate a retry
          setTimeout(() => {
            toast.message = 'Success!';
            toast.duration = 1500;
            // dismiss the toast in 2s
            setTimeout(toast.dismiss, 2000);
          }, 1000);
          // returning false so the toast doesn't dismiss
          return false;
        }
      }
    ]
  });
  await toast.present();
}

Toast Headers

Toast messages can now have headers as well.

In the above example, we specify a header as well as the toast message:

Thanks to Zach Keeton and Simon Hänisch for their contributions to this.

For more info, see Toast usage in the docs.

Bug Fixes

The slider component received some nice animation improvements and fixes in this release as well. We exposed animation events that can be passed in through the slide options to provide custom animations when swiping through the swiper component. See the Slides documentation for more details.

Angular devs will also be happy to know that the issue where the back button navigated back to the wrong tab has been fixed. Check out this PR for more details.

Some important bug fixes in this release surround datetime, and input label design. Check out the full release notes for a list of all the fixes.

Happy building! 💙

Read more

Apache Cordova (and later Adobe PhoneGap) emerged back in 2009 as a major evolution in app development. It allowed web developers like myself to reapply their web skills to create native mobile experiences for Android, iOS, and other platforms. In fact, I’ve been a long-time PhoneGap developer that loves the platform’s power and potential.

Even before I joined the Ionic team, one of the things I learned quickly was the importance of pairing Cordova/PhoneGap with a cross-platform UI framework like Ionic. In this post, I’ll share a few key reasons why every PhoneGap developer should be using Ionic (or something similar) to build their next mobile app.

Why PhoneGap and Ionic?

PhoneGap powers the capabilities that permit web code to run when embedded into a native app shell, as well as accessing native device features (camera, Bluetooth, etc.) using JavaScript.

While all of this is great, it’s what PhoneGap doesn’t give you that we are going to talk about today. When it comes to PhoneGap, the big thing that’s missing from the puzzle is the rest of the SDK and UI infrastructure that you need to build a high-performing, native-like app. Things like gestures, animations, modals, and button components. The technology also doesn’t offer built-in navigation or scrolling features—You’ll have to build all that yourself or use a UI toolkit.

Native SDKs vs PhoneGap

One such toolkit is the open source Ionic Framework. With over 100 UI components, plus navigation, platform-specific styling, and lots of other goodies that we’ll talk about here, it allows you to focus on your app’s core features instead of wrestling with a variety of concerns encountered when building for mobile devices.

Now, let’s jump into some of the major advantages of using Ionic if you’re a PhoneGap developer.

Platform Continuity

The first thing that web developers want to accomplish when building mobile apps is ensuring that the UI looks and feels like the platform on which the app is running. Additionally, developers want to implement Apple and Google-approved platform design styling with minimal effort, while users want a familiar, high-performing app experience. In fact, this is a typical criticism directed at hybrid mobile apps: If you simply throw your existing website into a native app shell, it’s noticeable.

An Ionic app viewed on an iOS device will automatically style itself with the iOS theme, while an Ionic app viewed from an Android device renders with the Material Design theme. When viewed as a Progressive Web App (PWA) from a browser, Ionic will default to using the Material Design theme. Also, deciding which platform to use in certain scenarios is entirely configurable and Ionic UI components automatically include platform-specific animations as well.

Android vs iOS Ionic styling

You could reproduce this on your own, but note the subtle design differences in the image above, starting with the “Home” header: The alignment is centered in iOS and is right-aligned on Android. Additionally, the icons in the tab footer have slightly different designs. When appearing across an entire app experience, these differences add up!

Responsive Design

The most compelling reason to build a web-based, cross-platform app is the ability to write one codebase and use it everywhere. This is easier said than done: You need to ensure that the app is responsive across many form factors. On mobile, with thousands of devices available today, this has never been more challenging.

Fortunately, Ionic’s UI components have built-in responsive design. Consider the Ionic SplitPane component, for example: When viewed on a larger, desktop-sized screen, it automatically expands to fill one-third of the available space. Within a smaller viewport, it displays a standard mobile hamburger menu.

Ionic SplitPane component

To test this out yourself, check out the Ionic 4 Conference app on GitHub, implemented in a variety of web frameworks including Angular, React, and Vue.

Constantly Changing Mobile Landscape

There’s no denying it—the mobile ecosystem evolves quickly. There are always new iOS and Android operating system releases to test your app on and thousands of mobile phones and tablets to support. On top of all that, there are occasional new hardware device form factors that are introduced, such as the iPhone notch.

Before joining Ionic, I wrote a guide specifically on the topic of handling device form factors after struggling to get my PhoneGap app to display correctly on the iPhone X. In order to get it to display properly, several steps were required:

  • Updating the viewport meta tag to ensure the entire iPhone screen is utilized
  • Switching to Storyboard splash screens to remove black bars at the top and bottom of the screen
  • Defining “safe area” perimeters so that the physical notch doesn’t hide the status bar
  • Applying a “safe area” for the footer

I struggled with this for hours, tediously researching and incrementally testing each change. Through a combination of Stack Overflow questions, an article from a well-known PhoneGap community member, and the official Webkit blog, I finally managed to cobble together a solution.

Was the effort worth it? In a certain sense, yes, because I needed to support an important new Apple design found in their flagship devices. However, the time I spent on these updates was time not spent building new features, answering user support emails, and so forth.

Fortunately, there’s a better way—A UI framework that has a proven track record of keeping up with the chaotic mobile ecosystem, so you can focus on building your app. Ionic automatically handles the iPhone notch details for you and keeps a constant lookout for any new designs handed down from Apple and Google.

iOS Notch example

What about learning Angular?

Until now, you may have held off adding a UI component library like the Ionic Framework for many reasons, including concerns around timing and effort involved. For me personally, I really wanted to move my PhoneGap apps to Ionic but was scared off at the time by needing to know Angular first.

However, with the recent release of Ionic 4, all major web frameworks are supported (including React and Vue), meaning Angular knowledge is no longer required to leverage Ionic. Additionally, Ionic 4 components even work without a web framework, which is perfect for PhoneGap developers looking to slowly incorporate Ionic into their mobile app.

Getting Started

Even if you started building PhoneGap apps around the same time I did (2013 or so), it’s easy to give the apps a fresh coat of paint by adding Ionic. All that’s required is a <script> tag to include the Ionic UI component library and a <link> tag to include Ionic CSS. See here for more details.

If you’d like to create a new Ionic app from scratch, install Ionic and start building!

Read more

Introduction

Welcome to the first post in a new series we’re kicking off all about building a full stack TypeScript app using Ionic, Angular and NestJS.

TypeScript is a powerful language that is a superset of JavaScript, with some additional features added to help build out large scale applications. One of my favorite features of TypeScript is its optional static type checking. I was a longtime static language dev (C#) before coming to the front-end, and even though I enjoyed the dynamic nature of JavaScript, I would often run into runtime errors and bugs in my client-side code because there was no type checking to help ensure the system I wrote was correct. TypeScript helps fix this by providing a way to add types to your variables and objects that are evaluated at dev time, but removed and turned into plain JavaScript when running in the browser.

Beyond type checking, TypeScript also helps you speed up your development by providing code completion and refactoring, as well as letting you use modern JavaScript language features that might not be available in all browsers yet.

Ionic and Angular developers have adopted TypeScript as their primary language for their client-side development for a few years now. However, TypeScript can run anywhere JavaScript can, which includes the server!

Front-end devs often turn to Node for their backend needs. The reasons are numerous, as Node provides a great runtime that works with the language with which they are most familiar (JavaScript). A couple of years ago, I was starting a new Node project and wanted to use TypeScript in it because I was curious if I could use TypeScript’s decorators to help define routes and place them directly on the controllers. Additionally, I liked this pattern from ASP.Net MVC and figured it would be possible with TypeScript.

It didn’t take much searching until I found a little, somewhat unknown, project called NestJS. Nest billed itself as a Node framework built with the power of TypeScript. I was immediately intrigued!

Meet NestJS

NestJS (just Nest from here on out), is a Node framework meant to build server-side applications. Not only is it a framework, but it is also a platform to meet many backend application needs, like writing APIs, building microservices, or doing real-time communications through web sockets. For us Ionic devs, it is a great solution to write our backends with because it fits in so well with the rest of our ecosystem.

Nest is also heavily influenced by Angular, and, as an Angular dev, you will immediately find its concepts familiar. The creators of Nest strived to make the learning curve as small as possible, while still taking advantage of many higher level concepts such as modules, controllers, and dependency injection.

However, this doesn’t mean that Nest is only for Angular devs. On its own, Nest is a powerful framework that anyone looking to build server-side applications on Node should consider. Moreover, Nest is also similar to other MVC (model, view, controller) frameworks out there, like ASP.Net MVC and Spring. Developers coming from other enterprise frameworks will find Nest familiar.

Since you use TypeScript (or JavaScript) to write a Nest application, one of the most intriguing features of Nest is the ability to use the same language on the client that you use on the server. This helps speed up development as your developers don’t need to context switch between the idiosyncrasies different languages can have. The unified language also opens doors for some potential code reuse between your front-end and back-end.

Are you interested in taking Nest for a spin and building out a backend for you Ionic apps? Great, let’s get started!

Our App

A typical full-stack tutorial has you build out a todo app. Todo apps are way too lame to waste on great tech such as Ionic, Angular, and Nest. So, in this tutorial series, we are going to build an app for space rangers 🚀. Our app will provide a list of missions a space ranger can go on, and have them enter their own missions. Once finished, they can mark their mission as complete.

You might be thinking this sounds like another todo app, but it’s not. It’s way cooler. It has space kitties. We will call it GoSpaceRanger.

Getting Started

I’m going to assume you are all set to get started with Ionic and Angular, but if not, head over to our getting started guide to get those setup.

First, create a new directory called go-space-ranger wherever you like to do your developer things and cd into it. We will create two applications (an Ionic and a Nest app) via CLIs in this directory.

Nest also comes with a CLI that is relatively similar to that of Ionic and Angular. Install it via NPM:

npm i -g @nestjs/cli

Use the Nest CLI to kick off a new app:

nest new gsr-server

When asked, select NPM as your package manager.

Go into the gar-server directory Nest created, and start up the development server by running:

npm run start:dev

This command kicks off a development server and loads your Nest app on a default port of 3000. Open up your browser to https://localhost:3000, and you should see:

Open up the gsr-server directory in your editor of choice. I’ll be showing VS Code in this tutorial and will talk about some tips on how to use it, but feel free to use what you are comfortable with. Let’s take a brief tour of the default Nest project.

The Nest Project Structure

There is nothing too crazy going on here. By default, Nest gives you a few goodies out of the box, that you would typically have to set up on your own. For code formatting and code style, Nest includes a Prettier and TSLint setup. Nodemon is included to recompile your app on each code change. Typescript is all set up as well. The config files for all these tools have sensible defaults, but feel free to modify them to fit your style.

The src folder contains all your app code.

The main.ts is the file that bootstraps your application. By default, Nest uses Express as the web framework to serve the HTTP requests. If you needed to do any additional configuration or add any Express middleware, you would do it here.

The app.module.ts file is familiar to Angular devs. The module serves the same concept here in Nest that it does in Angular. Modules provide logical separation in your app and give the configuration needed for each section.

When it comes down to it, Nest implements an MVC (model, view, controller) design pattern. In MVC, controllers listen in for incoming requests, call into services for data access and business logic, then serve out the models from the services back to the client.

The app.controller.ts file is a simple implementation of a controller:

Controllers are just pure ES6 classes that have a @Controller decorator on them. The decorator takes a string param that specifies the route the controller should listen for requests at. Since it is omitted here, this AppController listens to the root path ‘/’.

Nest also uses decorators on methods in a controller to designate which HTTP verbs (Get, Post, Put, Delete, etc…) a method should respond to. In our AppController, when a GET request comes in, Nest responds by invoking the getHello method.

Our controller has a service named appService injected into it. A Nest service is another ES6 class that is registered with Nest’s dependency injection framework as a provider.

This simple class has a single method that returns a string. If your server is still running (via NPM), go ahead and modify the string, then go back to your browser and refresh. Since the server is running in dev mode, Nodemon automatically recompiles and restarts the app every time a file changes.

Now that we know the basic structure of a Nest app, let’s create some of our own services and controllers.

Your First Nest Service and Controller

Our GoSpaceRanger app returns back a list of “missions” that our space rangers can respond to. So, to kick off our new app, let’s create a model to represent a mission. Create a new folder in src named models, then create a new mission.model.ts and paste in the following code:

export class Mission {
  id?: number;
  title: string;
  reward: number;
  active: boolean;
}

Next, we can use the Nest CLI to generate a service and controller for us:

nest g service missions
nest g controller missions

These commands will create a controller and a service file, along with spec files for testing.

For now, the Missions Service returns a hard-coded list of missions. Update the class in themission.service.ts file to the following:

@Injectable()
export class MissionsService {
  missions: Mission[] = [
    {
      id: 1,
      title: 'Rescue cat stuck in asteroid',
      reward: 500,
      active: true,
    },
    {
      id: 2,
      title: 'Escort Royal Fleet',
      reward: 5000,
      active: true,
    },
    {
      id: 3,
      title: 'Pirates attacking the station',
      reward: 2500,
      active: false,
    },
  ];

  async getMissions(): Promise<Mission[]> {
    return this.missions;
  }
}

Here, we have a few missions defined in an array and a getMissions method, which simply returns the missions array.

Next, update the missions.controller.ts controller to include the method that handles the GET request:

@Controller('missions')
export class MissionsController {
  constructor(private missionsService: MissionsService) {}

  @Get()
  getMissions() {
    return this.missionsService.getMissions();
  }
}

Now, if you back to your browser and visit https://localhost:3000/missions, you should see the list of missions returned in JSON format.

However, trying to access this data from a web app (like what we will build in a moment), you would get the following error in the browser:

This error is because the browsers same origin policy is kicking in and denying you to make an XHR request to a domain that the web page did not originate from. To get around this, we enable Cross-Origin Resource Sharing (CORS). Fortunately, Nest makes this easy, and all we have to do is open up the main.ts file and add app.enableCors() right after app is defined like so:

With that in place, we are ready to move forward making requests from our upcoming Ionic app. We will begin to build that out next.

Create GoSpaceRanger Ionic App

In your main project folder (the parent folder where we created the gsr-server Nest project), run the following command to create a new Ionic project:

ionic start gsr-client sidemenu

When asked if you want to install the AppFlow SDK, select no.

This command creates an Ionic project using the side menu template. Our app consists of a simple list page and a detail page, so starting with the side menu template gives us what we need initially, and we will use the side menu itself in the future.

Start up the development server:

ionic serve

After building the app, your browser should automatically open to https://localhost:8100/home and show you the blank starter:

Let us create a new Angular service via the Ionic CLI:

ionic g service services/missions

Look familiar? This code is nearly the same as our initial Nest service (the lone difference being the providedIn option passed in the Injector, which Nest doesn’t support yet).

In our service, we make the HTTP request to /missions and return the data. Update the class inmissions.service.ts with the following:

@Injectable({
  providedIn: 'root'
})
export class MissionsService {
  constructor(private httpClient: HttpClient) { }

  getMissions() {
    return this.httpClient.get('https://localhost:3000/missions');
  }
}

Since we are using HttpClient, make sure to add HttpClientModule (from @angular/common/http) in the app module’s list of imports.

Next, modify the home.page.ts file to call into the service and save the results to a local observable:

export class HomePage implements OnInit {
  missions: Observable<any>;

  constructor(private missionsService: MissionsService) {}

  ngOnInit() {
    this.missions = this.missionsService.getMissions();
  }
}

And to wrap it all up, replace the home.page.html template with the following:

<ion-header>
  <ion-toolbar>
  <ion-buttons slot="start">
      <ion-menu-button></ion-menu-button>
  </ion-buttons>
    <ion-title>Missions</ion-title>
  </ion-toolbar>
</ion-header>

<ion-content>
  <ion-list>
    <ion-item *ngFor="let mission of (missions | async)">
      <ion-label>
        <h2>{{ mission.title }}</h2>
        <p>{{ mission.reward | currency }}</p>
      </ion-label>
      <ion-icon slot="start" icon="rocket" color="primary"></ion-icon>
    </ion-item>
  </ion-list>
</ion-content>

Here, we have a reasonably simple Ionic page with a list that displays a few list items, each one with one of the missions coming back from the Nest API.

Now that our space rangers can view their missions, we will wrap part one of this series. So far, we’ve done a lot: We learned why TypeScript is beneficial not only on the client side but also on the server side. We also learned how to leverage the power of TypeScript on Node by using the Nest framework. And, we built a simple yet functional Ionic app that displays data served up from the Nest API. Exciting stuff!

If you would like to download what we have completed so far, grab the code on GitHub.

In part two, we will dive deeper into the various building blocks Nest gives us and build out more of our API—Stay tuned!

Happy Coding!

Read more

Ionic 4.2 is upon us, and with it, so is our second feature release of Ionic 4! This release is codenamed Helium, after the second element in the periodic table of elements.

Did you know? Helium is the second-most abundant element in the Universe (after hydrogen), making up about 24 percent of the universe’s mass. However, it is relatively rare on Earth. Scientists fear that we might someday run out of helium as it is considered a non-renewable resource. More info.

Ionic 4.2 landed on NPM today with a couple of highly-requested features and some great bug fixes. Let’s dive in and take a look.

New Features

New Error Event for ion-img

An event is now emitted on <ion-img> when an image fails to load. This can be useful to load default images in case the specified image is not found:

  <ion-img class="xt-lazy-img" src="https://d1zflb13a857us.cloudfront.net/assets/goodperson.jpg" (ionError)="loadDefault($event)"></ion-img>
  <ion-img class="xt-lazy-img" src="https://d1zflb13a857us.cloudfront.net/assets/badimage.jpg" (ionError)="loadDefault($event)"></ion-img>
loadDefault(event) {
    event.target.src = 'https://d1zflb13a857us.cloudfront.net/assets/img/default.png';
}

This feature was a pull request made by community member Ivan Tham. Thanks!

Optional ticks on ion-range

Before, when you specified snaps on <ion-range>, you would get tick marks on the slider bar. Thanks to a contribution from Seth Lilly, you can now specify a ticks boolean attribute to control if they appear or not (defaults to false):

<ion-range min="0" max="20" step="5" snaps="true"></ion-range>
<ion-range min="0" max="20" step="5" snaps="true" ticks="false"></ion-range>

Note that the ticks will only display if snaps is set to true, regardless of the value of ticks.

Bug fixes

There are a few notable bug fixes that are in 4.2.

Activated Route Observable Fixes

There was an issue where the observables on an ActiveatedRoute would only fire the first time the page was loaded. This is now fixed and fire every time the observable changes:

constructor(
  public actionSheetCtrl: ActionSheetController,
  private route: ActivatedRoute,
  private router: Router
) {}

ngOnInit() {
  this.route.queryParams.subscribe(qp => this.qp = qp);
}

DateTime Fixes

Lots of updates went into the DateTime picker this release. Defaulting to the local timezone and having the picker update the days in the month when you select a new month are a couple we think the community will be excited about:

For a full list of bugs fixes with links to issues, see the release notes here.

Special thanks to all our community contributors who helped by filing and fixing issues in this release, we appreciate it!

Whats next? We are heads down focused on bringing first-class desktop support to Ionic. Stay tuned!

Read more

© 2019 Extly, CB - All rights reserved.