Sunteți pe pagina 1din 76

ASP.

NET Core, NestJS, SwiftUI, AWS

MAY
JUN
2020
codemag.com - THE LEADING INDEPENDENT DEVELOPER MAGAZINE - US $ 8.95 Can $ 11.95

Stay at
Home!

Building Mobile Deploying Exploring NestJS


Apps Using APIs Using The Final Chapter
SwiftUI ASP.NET Core
Bonus tracks: Microsoft Teams, Power Apps, SharePoint

200+ Sessions
100+ Microsoft and industry experts
Full-day workshops Evening events

DEVintersection.com 203-264-8220 M-F, 9-4 EDT AzureAIConf.com


December 8–10, 2020
Workshops December 6, 7, 11
Las Vegas, NV
MGM GRAND

June 8–10, 2021


Workshops June 6, 7, 11
Orlando, FL
WALT DISNEY WORLD
SWAN AND DOLPHIN

SCOTT SCOTT
GUTHRIE HANSELMAN
Executive Vice President, Principal Program
Cloud + AI Platform, Manager, Web
Microsoft Platform, Microsoft

JULIA CHARLES KARUANA


LIUSON LAMANNA GATIMU
Corporate Vice President, Corporate Vice President, Principle PM Manager,
Microsoft Microsoft Customer Advocacy
Group, Microsoft
REGISTER EARLY
for a WORKSHOP PACKAGE
and receive a choice of
hardware or hotel gift card!
See website for details

KATHLEEN JEFF JOHN


Powered by DOLLARD FRITZ PAPA
Principal Program Senior Program Principal Developer
Manager, Microsoft Manager, Microsoft Advocate, Microsoft
AND MANY MORE
TABLE OF CONTENTS

Features
8 Modern Authentication Columns
With so many different definitions for authentication, it’s hard to know
what’s involved. Sahil provides the basics to get you up to speed.
Sahil Malik
52 T alk to an RD:
Ciprian Jichici and Markus Egger
16  se the MVVM Design Pattern
U Ciprian and Markus talk about what’s coming in artificial

in MVC Core: Part 1 intelligence and beyond.


Markus Egger
When you need your app to be reusable, testable, and maintainable, you
need MVVM in MVC. Paul shows you why and how to get the most out of it.
Paul D. Sheriff
74 CODA: On Forcing Functions
John V. Petersen

28  iscovering AWS for .NET


D
Developers
Julie takes an existing small ASP.NET Core API that uses EF Core and then
uses EF Core migrations to create a database on AWS, interact with the
Departments
database, and explore secrets management for the database credentials
both locally and for a deployed app. Soldiering On
6 
Julie Lerman
Markus talks about hard times before and after the pandemic
and quarantine.
 36 NestJS Step-by-Step: Connecting Markus Egger

NestJS with Angular (Part 4)


In this fourth article of the series, Bilal continues working on his ToDo list
14 Advertisers Index
as he connects an Angular front-end application with a NestJS back-end API.
Bilal Haidar 73 Code Compilers
 56 Introduction to SwiftUI
Wei-Meng uses the iOS’s declarative programming framework SwiftUI
to develop a simple user interface for iOS and macOS applications,
and teaches you how it works at the same time.
Wei-Meng Lee

US subscriptions are US $29.99 for one year. Subscriptions outside the US pay $49.99 USD. Payments should be made in US dollars drawn on a US bank. American Express,
MasterCard, Visa, and Discover credit cards are accepted. Bill Me option is available only for US subscriptions. Back issues are available. For subscription information,
send e-mail to subscriptions@codemag.com or contact Customer Service at 832-717-4445 ext. 9.
Subscribe online at www.codemag.com
CODE Component Developer Magazine (ISSN # 1547-5166) is published bimonthly by EPS Software Corporation, 6605 Cypresswood Drive, Suite 425, Spring, TX 77379 U.S.A.
POSTMASTER: Send address changes to CODE Component Developer Magazine, 6605 Cypresswood Drive, Suite 425, Spring, TX 77379 U.S.A.

4 Table of Contents codemag.com


EDITORIAL

Soldiering On
Times are dramatic. You don’t need me to tell you that. The issue you’re currently reading—and I’d like
to call it “the issue you’re holding in your hands,” but this may or may not be the case—was meant to
be one of celebration and happiness. This is our twentieth year of publication, and we had planned

various things to celebrate the occasion. All of labor of love, and a personal passion of mine. It’s how we managed to recover. I think it will be an
this now had to be pushed out. not a cut-throat competitive publication. We’ve interesting read and hopefully help people to
always gotten along well with other developer prevent this kind of attack from happening, or
For CODE Magazine, the last six to nine months publications. I even wrote for MSDN Magazine at least to recover from it as quickly as possible.
have been a rollercoaster of emotions. During (and others) myself. I had friends there. Many (Note: No data leaks occurred. No data was sto-
spring and summer 2019, we planned various pointed out that we should try to take advantage len. But our ability to operate the business was
new initiatives for our publication. We were excit- of the situation and move MSDN Magazine sub- greatly impacted.)
ed and things were going well. One of the major scribers to CODE Magazine as a great business op-
steps was a partnership with Microsoft that gives portunity. Instead, we offered a free subscription I also want to take this opportunity to apologize
all Visual Studio subscribers, as well as MS Dev to MSDN Magazine readers who were looking for to readers who experienced a delay in getting
Essentials users, access to CODE Magazine free of another option for a print publication. their magazine delivered or getting online ac-
charge. cess. We did our best to serve all of our custom-
All in all, things were still very good until mid-De- ers (including all of the new customers that have
Things were good and looking up. Then things cember. Just before Christmas, really. As we were come onboard from the Microsoft partnership),
took a bit of a step back for the magazine busi- in the process of wrapping up the year, we were and we realize that it isn’t the best way to start a
ness, when MSDN Magazine ceased to publish. hit by a massive ransomware attack. I remember new relationship, but the attack was so severe, it
Many thought this was good news for us, but I was coming back from my last customer meeting, simply wasn’t possible to proceed with business
sad. CODE Magazine is a side-business for us. A thinking it was time to lean back and relax during as usual.
the Christmas holidays. Instead, every single one
of our servers and workstations was wiped out All of this, of course, now pales in comparison to
by this attack. We’re still working through the COVID-19 pandemic. As I write this, I’m work-
the aftermath of all that. Once we’re ing from home and only being in contact with
completely back on our feet, my friends and colleagues online. That part was
we’ll publish a detailed relatively easy for us, as we’ve always been set
article about what up for remote working. It’s still strange to not be
happened and able to see anyone in person. And the fear that
friends, co-workers, or family may be infected is
crippling. The logistics of publishing a magazine
have been made much harder by this also. As I
write this, I don’t know whether our printshop
will be deemed essential enough to print this is-
sue. I also realize that there probably are more
important things to do at this point than print
a software developer’s magazine. At this point,
we’ll just have to wait and see.

However, the good news is that CODE Magazine


is also available digitally. We’re also working on
new CODE Magazine apps for iOS and Android.
In that sense, the timing couldn’t have been
better. I apologize to our print subscribers. We
may have to stop publishing print magazines for
an issue or two. But any possible interruption
is temporary. We will push forward and be back
operating normally. 

I think that will be true for everything. We will


all come together and soldier on together! Please
stay safe (and stay home).

 Markus Egger


6 Editorial codemag.com
OLD
TECH HOLDING
YOU BACK?

Are you being held back by a legacy application that needs to be modernized? We can help.
We specialize in converting legacy applications to modern technologies. Whether your application
is currently written in Visual Basic, FoxPro, Access, ASP Classic, .NET 1.0, PHP, Delphi…
or something else, we can help.

codemag.com/legacy
832-717-4445 ext. 9 • info@codemag.com

codemag.com
ONLINE QUICK ID 2005021

Modern Authentication
What does this “Modern Authentication” phrase mean to you? This is the challenge. Something so critical to our applications
doesn’t even have a good definition. I’m confident that now, many of you are wondering what this article is all about. This article
is about the absolute basics of authentication. When was the last time you wrote an application that didn’t need any information

about the user using the application? Almost never, right? worker has been. They’re just working from home. They just
Authentication is as essential as it gets. And yet the chal- fire up Microsoft Teams or similar, and it’s business as usual.
lenge is mixed with the jargon of keywords and chasing In fact, I’d argue that working remotely, done right, is more
ever-changing standards of security and identity. The field productive than sitting in an office, dealing with a commute
of authentication can appear overwhelming. for two hours a day, paying for, and maintaining all that
real estate, etc.
The result is poorly written applications. Think about it,
poorly written applications that concern user identity. Ouch! All of this is made possible because companies today have
the confidence that they can fire up Microsoft Teams or their
Sahil Malik The goal of this article is to simplify all of this, so you’re browsers, or other applications, authenticate to the corpo-
www.winsmarts.com better equipped to talk back smartly when someone throws rate network securely, and work. Although this act of join-
@sahilmalik all that technical identity jargon at you. The goal is to share ing an online meeting or opening a document on SharePoint
the absolute basics of identity that I wish everyone was on may appear banal, there are two very important tenets at
Sahil Malik is a Microsoft play here:
the same page about.
MVP, INETA speaker,
a .NET author, consultant,
• The organization is confident that this data is going
and trainer. What Is Modern Authentication? to be secure.
Sahil loves interacting with Let’s start with the first challenge. There’s no clear defini- • The employee or contractor finds this experience easy
fellow geeks in real time. tion of this term “Modern Authentication” and yet everyone and convenient to use.
His talks and trainings are throws the term around. So let’s attempt to define it first, at
full of humor and practical least for the purposes of this article. Although enabling such seamless productivity wherever you
nuggets. are, or whoever you are, and whatever device you are using,
The challenge is that we’ve had pretty good solutions for is the work of many technologies and many disciplines, a
His areas of expertise are authenticating and managing users for a while now. In the key enabler here is modern authentication.
cross-platform Mobile app Microsoft world, we’ve used Active Directory. But the con-
development, Microsoft cepts aren’t unique to active directory. There are protocols With that long-drawn out background, let me attempt to
anything, and security such as NTLMv2 and Kerberos that have been in use for some define what I mean by modern authentication.
and identity.
time now. The problem is that neither of these protocols are
perfect for an Internet world. Legacy Authentication
Legacy authentication is what we used for our dinosaur ap-
NTLMv2 relies on point-to-point exchange; to simplify, with- plications—protocols such as NTLMv2, Kerberos, or similar,
out sending my password over the wire, using some sort that you’ve typically used on-premises, are legacy authen-
of shared understanding between me and the recipient tication.
(server), the server is able to ascertain that I’m providing
the correct credential. This has a serious downside in that No, I’m not calling them extinct, they’re still our bread
my credentials can’t be forwarded by the server to another and butter. And those dinosaurs are being brought into
server. the modern authentication world kicking and screaming,
whether they like it or not.
To alleviate that issue, a protocol called Kerberos was creat-
ed. In Kerberos the domain controller hands out tickets for The reality is that organizations have a lot of investment in
creating sessions between the client and the server or the these classic/legacy identities. And as much as we’d like to,
domain controller itself. Kerberos is great and has served us many critical products, including those that run in Azure or
very well in corporate environments. Where it breaks down, AWS, don’t yet support modern identities properly. There’s
of course, is when you try to take things to Internet scale. a lot of investment in bridging this gap. For instance, there
For instance, Kerberos is extremely chatty and requires a lot are things such as Azure AD connect that let you bridge on-
of network ports to be opened for it to work. Now, organiza- premises Active Directory identities to Azure AD identities,
tions have really stretched the boundaries of what Kerbe- or products such as SQL in Azure or Azure Storage, that are
ros can do. There are concepts such as trusts, VPNs, and so making progress in working with modern identities.
much more that have enabled us to stretch the boundaries
of what was intended to be a single-organization solution. A lot of work still needs to be done here. I don’t have rose-
colored glasses on, and I must assert that these legacy iden-
The modern workplace is different. As I write this article, tities will be with us for quite a while.
the world is hunkering down due to the COVID-19 pandemic,
and a lot of businesses are getting really hurt. Restaurants, Modern Authentication
airlines, cruise ships—I can’t imagine the damage they’re Modern authentication refers to authentication established
experiencing right now. But compared to similar previous by protocols that are better designed for Internet scale and
crises, I’m simply amazed at how unfazed the information management. Although I realize that this is a very amor-

8 Modern Authentication codemag.com


phous definition, there are some common tenets among all WS-Fed
of these authentication protocols.
WS-Federation, or WS-Fed for short, was an early entrant
• They allow you to separate the identity provider (IdP into this space. The problem it was trying to solve was a
for short) from the service provider (SP for short). website, such as SharePoint, that needed authentication
The IdP is the entity who accepts your credentials and services from an identity provider, such as Azure AD. Here’s
validates who you are. The SP, also sometimes referred how it works at a high level.
to as the relying party (or RP), is the one providing a
service. For instance, an IdP could be Azure AD, who The user opens a browser and visits the RP. Are you getting
authenticates you. And an RP or SP can be a Share- used to these acronyms yet? RP is the relying party, the ap-
Point website that accepts such identities. plication that relies on the IdP for authentication services.
• They don’t require any direct communication between RP, SP, same thing.
the IdP and SP. No need to open firewall ports, etc.
Authentication is built on the backbone of trusts, pre- The user sends an anonymous request through their brows-
established ahead of time using mechanisms such as er.
certificates. SharePoint trusts the Azure AD identity,
and homework has been done ahead of time to ensure The SP says, hey, you’re not authenticated, but here are the
that the identity has not been tampered with. kinds of identities I accept.
• The process is almost always asynchronous and state-
less. If there’s need for state, it’s the responsibility of The user picks one of those choices; this process is called
the client. There are some borderline scenarios where Home Real Discovery or HRD. This can be automatic, or man-
this isn’t possible, such as when you need instant sign ual, or any combination thereof.
out. But in a vast majority of cases, the entire protocol is
stateless. And it’s asynchronous, which means that the The user is then redirected to the IdP. The user then pro-
SP may not even know if the user will complete the au- vides their credentials, and they’re issued an SAML token.
thentication process, and the IdP has no idea if the user Wait, SAML? Yes, keep reading!
will ever go back to the SP with the provided identity.
This token is then presented to the SP, which is able to vali-
These general characteristics are exhibited by numerous pro- date it and extract the user’s identity and necessary claims
tocols, all of which are modern authentication. Some of these out of it.
protocols are WS-Fed, SAML, OAuth, and OpenID Connect.
Azure AD supports these protocols, and the various endpoints At the end of this entire dance, a Web browser session is
can be seen by clicking the “endpoints” button on any app established between the SP and the user. At no point did
page in the Azure Portal, as can be seen in Figure 1. the SP have to make a direct call to the IdP. And to continue
the session, well it’s just a browser session, so your usual
Let’s understand them one by one. concepts apply.

Figure 1: The various authentication endpoints

codemag.com Modern Authentication 9


behalf frequently go way beyond “access my name and email
SAML address.”
SAML, well if I described the flow step by step, you’d say,
hey this is just like WS-Fed. And indeed, in many ways it is. If you pay close attention, just to leave a comment on an ab-
But there are a few additional points involved. surd incendiary article on the Internet, you are required to:

First, SAML works with SAML 2.0 tokens. Wait, isn’t this con- • Give your name and email address
fusing? Is SAML a protocol or a token? It’s both. WS Fed uses • Give your “friends” list
SAML 1x tokens, and SAML protocol uses SAML 2x tokens. • Let them access your information at any time
• Allow them to post on your behalf
Secondly, SAML gives you a lot more flexibility around who • Draw your blood (just kidding!)
starts the whole flow, what claims are needed for the ap-
plication to work, and how they are encrypted. Clearly, there has been abuse. And this has forced the hand
of many of these companies to limit the abuse.
Finally, SAML is newer, so it was back-fitted into scenarios
it was never intended for, such as SAML to JWT token trans-
formations or vice versa, or making SAML work on mobile OpenID Connect
platforms. OpenID Connect is a more formalized version of OAuth,
where the world agreed to agree on certain minimum stan-
As of today, SAML continues to be a very important protocol dards that protocols must follow for each major platform.
SPONSORED SIDEBAR: and will remain so for many years to come. They agreed on a certain basic set of tokens, and some basic
claims that must be present in those tokens that the world
Are Your Legacy Apps can rely on to know the user’s identity.
Stuck in the Past? OAuth
OAuth, isn’t an authentication protocol. It’s a delegation As of today, OpenID Connect is perhaps the main protocol
Need FREE advice on protocol. that all new development is being done under. Standards
migrating yesterday’s are powerful and it makes a lot of sense to get behind the
legacy applications to
Have you ever landed on a site where someone published standards. Azure AD v2 flows, for instance, are OpenID
today’s modern platforms?
an article so absurd that you were compelled to comment? Connect compliant. The main thrust of this article will be
Take advantage of CODE
Around the comment box, it says that you need to log-in OpenID Connect.
Consulting’s years of
experience migrating first. And some of the options include logging in using your
legacy applications by Facebook, Twitter, or Google accounts. There are three main things you need to know about OpenID
contacting us today to Connect:
schedule your free hour Here’s how that works. Let’s say that you pick Facebook. The
of CODE consulting with site then redirects you to Facebook, where you provide your • Tokens
our expert consultants. credentials. And then you’re redirected back to the site (the • Public client vs. confidential client
Get answers! No strings. SP), which now knows who you are and lets you log in. • Grant types or flows
No commitment.
For more information Here’s what’s really going on. You’re redirected to Facebook, Tokens
visit www.codemag.com/ not to authenticate yourself, but for the application to do In OpenID Connect, you perform token exchange. There are
consulting or email us certain things on your behalf. But who are “you” in that three kinds of tokens you should know of. Before I mention
at info@codemag.com. “your behalf” part? Well, that’s where you need to authen- what kinds of tokens, I should mention that the most common
ticate yourself. And what are those “certain” things? It’s format you’ll see for these tokens is the JWT format. Although
things like accessing your name and email address. This al- JWT isn’t a hard and fast requirement, it’s the most common
lows the application to know who you are, so it sort of works format you’ll run across. JWT is simply three JSON strings en-
like an authentication protocol, even though it was never coded in base64 format with the “.” character between them.
intended to be one. The first part is the header, the second is the body, and the
third is the signature. When an IdP issues a JWT token, it signs
In that sense, OAuth is a pseudo-authentication protocol. it with its private key, and using its public key, the consumer
of that token can ensure that the token hasn’t been tampered
with. It’s as simple as that. Oh yes, one other very important
thing, these tokens are sent over various protocols and must
In a sense, OAuth is a pseudo- not be leaked. The world has agreed that end-to-end encryp-
authentication protocol. tion, such as HTTPS/TLS, is a 100% hard-and-fast requirement
for these tokens to remain secure, so please keep that in mind.

Given that, there are three different tokens to know of: ID


The problem with OAuth is, of course, first that it’s such a tokens, access tokens, and refresh tokens.
loose standard that everyone has chosen to implement it
however it fits them. This has caused a real confusion and An ID token is the user’s identity. An ID token must not contain
interoperability issue. any authorization information—it’s merely an identifier for the
user. When you sign into a website, you’re given an ID token.
The second problem is that it was promoted heavily by so- That’s how the website is able to ascertain who you are.
cial media companies. Their intent wasn’t to provide a social
service, but to force you to overshare. So, those “certain The second token you must know of is an access token. This
things” that you’re allowing the application to do on your token lets you authenticate yourself to a Web API. You put

10 Modern Authentication codemag.com


this token in the Authorization header of your request, remember, as a client application, you must never unpack
which usually looks like Bearer goobledoogook, that the the tokens. You must treat tokens as opaque. This is because
API you’re calling can verify and then grant you access. Be- at any point, the IdP can choose to change their implemen-
cause losing an access token pretty much means losing the tation to encrypt the tokens. This will make it effectively im-
keys to whatever that access token is good for, access to- possible to unpack the token on the client side. If you build
kens are usually short-lived (30 minutes to one hour, etc.). your application around unpacking access tokens, you’re
It’s difficult, although not impossible, to centrally revoke setting yourself up with a time bomb.
access tokens. There’s a specific claim in an access token
called the jti claim, which is a unique identifier for the jwt
token. Using this claim, you can effectively block compro-
mised access tokens also. Now, you may be wondering, if If you build your application
you have authored a Web api, how do you validate an access
token?
around unpacking access tokens,
you’re setting yourself up with
Finally, a refresh token is a long-lived token that you use to a time bomb.
get new access tokens. You usually get an access token for
a certain resource—also known as an audience. Only clients
that can safely secure refresh tokens should use refresh to-
kens. This is why a refresh token isn’t included by default. On the server, you validate the token by verifying the value
You have to ask for it using a specific scope called “offline_ of the various claims. For instance, is the token still valid
access.” I haven’t talked about scopes yet, so keep this in from a time perspective, which is a combination of three
the back of your mind for now. claims: nbf (not before), iat (issued at), and exp (expira-
tion). Whether you are the valid audience for this claim is in
Validating Tokens the “aud” claim. Who issued this claim is in the “iss” claim.
Validating a token is an important part of this puzzle. A And so on.
refresh token is something you just store and use as a key to
unlock future access tokens. You do need to validate ID to- Public Client vs. Confidential Clients
kens and access tokens. How can you trust that a token pre- Many OpenID Connect flows require you to present a secret.
sented by a user or application is something you can trust? The secret may be a password, or it may be a certificate. A
public client is simply an application that runs on a platform
Well, there are two kinds of tokens: opaque and not opaque. that you can’t trust. For instance, a browser, or a mobile app
Opaque tokens are, by reference, tokens, i.e., you’re given deployed on a phone that you have no control of, or a Win-
a random string and you’re required to call the IdP every dows desktop app, etc. These applications can’t be trusted
time you wish to validate a token. The advantage here is in- to hold a secret.
stant central sign-out and revocation, and possibly smaller
token size. The disadvantage is much higher network traffic. The contrast to this is a Web application, which can store
But, because you’re submitting the token to the IdP at every a secret securely on the disk (or elsewhere) on the server.
step, the validation of the token isn’t something you need Because this secret never leaves the server, you can trust
to worry about. that it’s never compromised. Such an application is called a
confidential client.
On the other hand, a non-opaque token requires you to
put in some work to validate it. Note that most SDKs have This is an important distinction because in Azure AD, public
implemented this for you, but it still helps to know how it clients are disabled by default. You have to explicitly en-
works. As I mentioned, the incoming token has three parts. able them for you to request access tokens without a secret.
The first step is to decode the token and validate its sig- This can be seen in Figure 2.
nature. To validate the signature, you grab the jwks_uri
from the well-known configuration of the OpenID Connect Flows or Grant Types
provider. What? Did I just speak out a bunch of jargon? Finally, you need to understand the concept of grant types.
Sorry! In reality it’s quite simple. Every IdP exposes a well- OpenID Connect isn’t a single protocol. It’s a family of ever-
known configuration endpoint. For instance, for Azure AD, growing protocols. Depending upon the kind of application
the endpoint looks like this: https://login.microsoftonline. you’re authoring, you may want to pick the appropriate
com/{tenant}/v2.0/.well-known/openid-configuration. Or grant type. Here are some common grant types you need
sometimes you may have app-specific configuration URIs, to know about.
like this: https://login.microsoftonline.com/{tenant}/
v2.0/.well-known/openid-configuration?appId=<appId>. Client Credential Flow is when you care about a headless
This URL is anonymous and it returns a simple JSON object. process authenticating, where you have no opportunity
There will be a property called jwks_uri, which contains the to present a user interface. This is a simple POST request,
public keys for signature validation. where you specify what you’re requesting a token for, also
known as the audience URI. You specify who you are, which
Lesson learned. For your SP to validate tokens, it needs out- is the client ID, also known as App ID, and you provide a cli-
bound connectivity to the well-known configuration end- ent secret. The caller of the client credential flow has no op-
point URI and the jwks_uri. You can’t validate AAD tokens in portunity to provide an inline consent, so all consents must
an air-gapped environment. already be in place. Also, the caller of the client credential
flow in Azure AD can be either an application or a service
Once you’ve validated the signature, you can now unpack principal. When acting as a service principal, you simply
the token. This simply means deserializing the token. But provide the service principal username and password, which

codemag.com Modern Authentication 11


Figure 2: Treating the application as a public client

Listing 1: Client Credential Flow • And what if the application is cloned? With ROPC,
POST /{tenant}/oauth2/v2.0/token HTTP/1.1 you’re opening your users to be phishable. You have
Host: login.microsoftonline.com no idea if the application is closed or not.
Content-Type: application/x-www-form-urlencoded • Performance will be bad because you’ll have hashing
encryption of credentials.
client_id=<guid>
&scope=https%3A%2F%2Fgraph.microsoft.com%2F.default • Your server can’t distinguish the user from the app.
&client_secret=<secret> • Credentials are exposed to the client app. Credentials
&grant_type=client_credentials should never be anywhere else except the user and
the server. Frankly, when you can help it, not even the
user. Passwordless is what you should strive for.
• The client can now request any scope without an okay
Listing 2: Request for an auth-code
from the user. So, imagine that you can now access
https://login.microsoftonline.com/ the calendar app without the user knowing you’re ac-
{tenant}/oauth2/v2.0/authorize? cessing the calendar.
client_id=<clientid>
&response_type=code • There’s no support for federation.
&redirect_uri=http%3A%2F%2Flocalhost%2Fmyapp%2F • There’s no support for MFA.
&scope=openid%20offline_access%20https%3A%2F%2Fgraph.microsoft.com%2Fmail.read • Advanced threat protection and conditional access be-
&state=12345 come less useful.
• There’s no SSO.
• It’s against the OAuth best practice guidelines
then also allows you to do interesting things, such as roles (RFC8252)
on headless processes. An important note about client cre-
dential flow: the access token returned is valid for an hour, Long story short, if someone asks you to use ROPC, run like
which is the default, but has configurable duration in Azure hell. Don’t do it.
AD. However, there’s no refresh token. To get a new access
token, you simply repeat the request. The request to get Here’s an interesting side note. Did you know that the Pow-
an access token using client credential flow can be seen in erShell commandlet Connect-MSOLservice, when logging in
Listing 1. For the eagle-eyed among you, the scope I’ve re- using saved credentials, uses ROPC behind the scenes? It’s a
quested here is https://graph.microsoft.com/.default. This good thing they deprecated the MSOnline module.
will give me a token for all scopes that have been consented
to. You’re required to use the ./default shortcut in CC flow. Authorization Code Grant Flow is a pretty good protocol.
It’s perfect for signing into a website and requesting ac-
Resource Owner Password Credentials grant flow, or ROPC, cess tokens for secure Web APIs. It works in two steps. First,
is where you play a username/password to an endpoint. you redirect the user to the authorization endpoint, where
Long story short, try not to use it. You have to use it when the user provides their credentials and authenticates them-
you must have 100% control on the UX for sign in, but the selves. Hold on! I thought authorization and authentication
downsides far outweigh the upsides. Here are some reasons were separate concepts? Yes, they are, and this nomencla-
why you should avoid using ROPC: ture can be off putting. But remember, an authorization step
is when you have a chance of showing a user interface, and
• ROPC is very hacker-friendly, and you’re taking on a there is where the user authorizes that such and such appli-
big responsibility for securing a much larger attack cation is allowed to do such and such actions. This process
surface now. For instance, it’s now your responsibility is called consent. So the name, authorization endpoint,
to protect against brute force, password spray, dic- makes sense. The request to the authorization endpoint
tionary attacks, etc. looks like Listing 2. It’s worth mentioning that requesting
• You’re between a rock and a hard place—the rock the auth-code is just one possible mechanism here.
being taking responsibility to secure the user’s cre-
dentials on a public client and the hard place being There are some interesting things going on in Listing 2.
prompting the user for frequent sign ins. Guess what Let’s try to understand them. First of all, it’s a simple GET
most people do then? Store credentials in public cli- request. The request must include things like who are you
ent, which is a bad idea!!! (client ID), what you’re asking for access to (scope), and,

12 Modern Authentication codemag.com


optionally, a state. State is simply passed back to you at the Device code flow is where the device that’s authenticating
end of a success or failure so you can remember the state has a very limited UI. It simply shows you a code and you’re
at the origination of the authentication request. This means required to open your browser on a laptop, visit a specific
that if the user landed on a deep link in your application, URL, enter that code, and finish the authentication process.
the state is a perfect place to store that deep link. In the meantime, that device with the limited UI polls an
endpoint on the IdP, waiting for you to finish the authenti-
Of special interest is the redirect URI. The redirect URI is cation process, at the end of which it gets the access token
where the IdP, in this case Azure AD, returns the auth code. and optionally a refresh token.
This URI must be something you control and trust. It must
be HTTPS. A common mistake is to leave straggling redirect There are, of course, additional details and flows to talk
URIs in your app, such as something.azurewebsites.net. Re- about, but like any other topic, identity is a universe that
member, when you deprovision that azurewebsites.net URL, you can keep diving deeper into. I’ll stop here for now.
someone else can claim it, and hijack your application, be-
cause you forgot to remove that redirect URI. So keep that
redirect URI list clean and at the bare minimum of what you Scopes and Consents
absolutely need. I couldn’t finish without briefly mentioning this important
concept. Scopes is how you shape an API, or how a client
This request, if successful, returns an auth code, which is requests certain claims. When I request the OpenID scope,
simply in the query string in the “code” parameter. Now you I’m asking for the bare minimum claims that OpenID sup-
can use this one-time-use-auth-code to request an access ports. But when I request the profile scope, I’m asking for
token. The request to get an access token can be seen in the user’s name, email address, etc. Similarly, when I ask
Listing 3. for the offline_access scope, I’m asking for a refresh token.

The request for an access token, if successful, returns an ac- Scopes also allow me to request access tokens, where the
cess token to all the scopes you have previously consented scp claim of the scope includes the audience against which
to. There are some important things to know about the re-
quest you see in Listing 3.
Listing 3: Asking for an access token
First, that it’s a POST request played to the token endpoint. POST /{tenant}/oauth2/v2.0/token HTTP/1.1
If you asked for a refresh token in scopes, you can also play Host: https://login.microsoftonline.com
that refresh token to this endpoint to get new access tokens Content-Type: application/x-www-form-urlencoded
without asking the user to sign in again. You request a re-
client_id=<appid>
fresh token using the scope offline_access. Effectively, this &scope=https%3A%2F%2Fgraph.microsoft.com%2Fmail.read
means that the user allows you to access functionality on &code=<authcode>
their behalf, when offline. That is, the user doesn’t need to &redirect_uri=http%3A%2F%2Flocalhost%2Fmyapp%2F
sign in every time you ask for an access token. A common &grant_type=authorization_code
&client_secret=<secret>
poor architecture is to store access tokens on durable stor-
age, or to reuse refresh tokens across multiple nodes via
durable storage, such as databases. This is a terrible idea
and breaks the concept of non-repudiation.

The second thing you should note in Listing 3 is that the


auth code is a one-time-use code. This is for security rea-
sons. In this code, the redirect URI must match, down to
the trailing slash, the same redirect URI you used to request
the auth code.

The final thing to note about Listing 3 is that the client_se-


cret you need to provide is only for confidential client appli-
cations. You can toggle the application to be a public client
and not require the client secret to be passed.

Next up is the Implicit Grant, which is suitable for Java


Script SPAs. Implicit grant isn’t recommended anymore, but
where you can’t control the back-end, such as on a Share-
Point online site, and you must make Web service calls to a
CORS endpoint from a JavaScript SPA, implicit grant is your
only choice.

PKCE, or proof key for code exchange is where you supply a


code at the beginning of your auth flow. When requesting
access tokens, you must supply the same code. This miti-
gates many of the downsides of implicit grant and is com-
monly used on mobile applications. Work is underway on
numerous identity providers to support PKCE on JavaScript
SPAs as well. Figure 3: Granting consent during login

codemag.com Modern Authentication 13


Figure 4: Granting admin consent via the portal.

my access token must work. For instance, a valid scope is consent. Consent can be granted either during log in, by the
https://graph.microsoft.com/user.read. When I ask for this user, as can be seen in Figure 3, or by an administrator dur-
scope, I’m asking for an access token that has the ability to ing log in, ahead of time via an API, or via the Azure Portal,
read the currently logged-in user’s profile on an API called as can be seen in Figure 4.
Microsoft Graph.
Some specific scopes require an administrator consent. For
This means that the user must allow the act of reading the instance, you wouldn’t want some random user in your or-
user’s profile being read. This allowing act is called granting ganization to grant the permission to register new apps,
right?

Summary
ADVERTISERS INDEX This article is loaded with information, so if you read this
far, congratulations. I don’t pat myself on the back often,
but the information in this article is very hard to find in a
Advertisers Index succinct, accurate, and easy to understand manner. I wish
this basic information about identity was common knowl-
CODE Consulting edge to everyone. It would really make many tasks so much
www.codemag.com/consulting 76 simpler.

CODE Framework Authenticating a user to your application is about as basic a


www.codemag.com/framework 27 requirement as it gets. Hardly any application doesn’t have
CODE Legacy to deal with it. It’s appalling how confusing it can be, given
www.codemag.com/legacy 7 the complexities of the protocols and myriad platforms that
make it so much more complex. There are some companies
CODE Magazine that have done a stellar job at making this dev story easier,
www.codemag.com/subscribe 71, 75 but you still need to know the concepts.
CODE Mobile
I hope the concepts in this article help you get further
www.codemag.com/mobile 35
Advertising Sales: ahead in solving identity challenges properly. And when ar-
Tammy Ferguson CODE Staffing ticles talk about IdP, SP, RP, SAML, redirect URI, and auth
832-717-4445 ext 26
tammy@codemag.com
www.codemag.com/staffing 15 code, you’ll be well equipped to know what they are talking
about.
DEVintersection Conference
www.DEVintersection.com 2
Until next time, code securely.
dtSearch
www.dtSearch.com 25  Sahil Malik

LEAD Technologies
www.leadtools.com 5

This listing is provided as a courtesy


to our readers and advertisers.
The publisher assumes no responsibi-
lity for errors or omissions.

14 Modern Authentication codemag.com


ONLINE QUICK ID 2005031

Use the MVVM Design Pattern


in MVC Core: Part 1
This article presents how to use the Model-View-View-Model (MVVM) design pattern in MVC Core applications. The MVVM
approach has long been used in WPF applications but hasn’t been as prevalent in MVC or MVC Core applications. This
article illustrates how using MVVM in MVC makes your applications even more reusable, testable, and maintainable.

You’re going to be guided step-by-step building an MVC elements on the page, the POST method in the controller
Core application using the Entity Framework (EF) and a view accepts the view model as its parameter and all the bound
model class to display and search for product data. properties are filled in automatically.

From within the controller, call a method in the view model


The Model-View-View-Model to get or save data, or any other command you need. Your
Approach controller methods are thus kept small because the code to
The reasons programmers are adopting the MVVM design load the page, and to save the data from the page, is all
pattern are the same reasons programmers adopted Object within the view model. Data binding takes care of all the
Paul D. Sheriff Oriented Programming (OOP) over 30 years ago: separation rest.
http://www.pdsa.com of concerns, reusability, maintainability, and testability.
Wrapping the logic of your application into small, stand- Another big advantage of using a view model class is that
Paul has been in the IT alone classes allows you to reuse those classes in many dif- unit testing is easier. There’s no need to create an instance
industry over 33 years. ferent applications. Maintaining logic in classes allows you of, or test, any methods in the controller. If all the control-
In that time, he has suc- to fix any bugs in just one place and any other classes us- ler is doing is calling methods in the view model, then you
cessfully assisted hundreds just need to test the view model.
ing that class automatically get the fix. When you don’t use
of companies to architect
global variables in an application, testing your application
software applications to
becomes much simpler. Wrapping all variables and logic that Many of us have been developing for many years and we’ve
solve their toughest business
problems. Paul has been operates upon those variables into one class allows you to seen technologies come and go. However, the basics of
a teacher and mentor create a set of tests to check each property and method in how you create classes with methods and properties hasn’t
through various mediums the class quickly and easily. changed. If you keep any technology-specific objects such
such as video courses, as Session, ViewData, TempData, etc., out of your view mod-
blogs, articles, and speaking els, then moving from one technology to another becomes
engagements at user easier. For example, I’ve had many clients create an MVC
groups and conferences Any other classes using the class application, then decide that they want to create a part of
around the world. that application in WPF for their data-entry people. I only
Paul has 23 courses in the
automatically get the fix. had to create the UI in WPF and simply reuse the view model
www.pluralsight.com library classes that had already been tested.
(http://www.pluralsight.
com/author/paul-sheriff) The key to MVVM in MVC is moving logic out of the MVC con-
on topics ranging from troller and into a view model class that contains proper- The Tools You Need
JavaScript, Angular, MVC, ties that you can bind to the user interface. The controller If you follow along with the steps in this article, you’re
WPF, XML, jQuery, and should only call a method in your view model, and periodi- going to build an MVC Core application. The application
Bootstrap. Contact Paul cally set a property or two prior to calling that method. Nor- is going to show you how to interact with a product table
at psheriff@pdsa.com. mally, properties that don’t need to be set as view model in a SQL Server database. I’m going to use Visual Studio
properties will be bound to controls on your user interface. Code, MVC Core, C#, the Entity Framework and the Adven-
To ensure reusability of your view model classes, don’t use ture Works Lite sample database for SQL Server. You can get
any ASP.NET objects such as Session, ViewBag, TempData, the Adventure Works Lite database on my GitHub at https://
etc. in your view model. github.com/PaulDSheriff/AdventureWorksLT.

In the GitHub repository there’s a file named Adventure-


Why Use MVVM in MVC WorksLT.bak that you can use to restore the SQL Server
When using MVVM in WPF, you typically have a view model database. If you’re unable to restore the backup, there’s
class for each screen. In MVC, you do the same by having a SalesLT-Product.sql file that you can use to create the
one view model class per page. Of course, there can be ex- product table with data in any SQL Server database.
ceptions, but this separation keeps your controllers and
view model classes small and focused. The controller’s GET
method creates an instance of the view model (or has one The Overall Project
injected) and passes it as the model to the page. The page For this article, you’re going to be creating the four projects
binds to properties on the view model as HTML input or shown in Figure 1. This architecture is one that I’ve found
hidden elements. These bound properties may come from to be flexible, reusable, and testable. The four projects are
an instance of one of your EF entity classes, or additional the MVC Core application, and one class library each for your
properties that are specific to that page. If you have input view model classes, data layer classes, and entity classes.

16 Use the MVVM Design Pattern in MVC Core: Part 1 codemag.com


Figure 1: A typical project structure for your .NET applications

For many of you, the only thing that might be a little differ- For example, I’m going to create my project under the D:\
ent is the addition of the view model classes. The focus of Samples folder. Once I open a terminal window, I type in
this article is to show how to simplify your controllers and the command:
move most of your logic into view model classes. A second-
ary focus is to illustrate how to build a multi-layered project CD D:\Samples
in Visual Studio Code with .NET Core.
Once you’re in your development folder, create a new folder
In this article, you’re going to be using the SalesLT.Product named MVVMSample using the MKDIR (or the MD) com-
table in the AdventureWorksLT database. You’ll first learn to mand:
build an HTML table to list all products in this table. Next,
you add search capabilities so you can search on one or mul- MKDIR MVVMSample
tiple columns. The logic to build the listing and searching is
created in the view model class instead of in the controller. Change to the new directory using the CD command in the
terminal window, as shown in the command below.
The ProductController class (1a in Figure 1) has both an
instance of a ProductViewModel class (2a) and an imple- CD MVVMSample
mentation of an IProductRepository (3b) injected into it by
MVC Core. The implementation is a ProductRepository class Create a new MVC Core project in the MVVMSample folder by
(3c), which also has an instance of the AdvWorksDbContext executing the dotnet new mvc command.
class (3a) injected into it by MVC Core.
dotnet new mvc
The ProductRepository is passed into the constructor of the
ProductViewModel class by the DI service in MVC Core. A Once the command has run, open the folder in VS Code by
method named HandleRequest() is called on the view model clicking the File > Open Folder… menu. A few seconds after
to retrieve the data from the Product table using the Entity opening the folder, you should be prompted with a dialog
Framework, which is implemented in the ProductRepository that looks like Figure 2. Click on the Yes button to add the
class. Each row of data in the Product table is represented required assets to the new project.
by the entity class Product (4a). A generic list of Product
objects is placed into a Products property of the Product-
ViewModel. This view model class is used as the model to the
Products page to create the HTML table.

Create the MVC Core Project


Let’s create our MVC Core project using Visual Studio Code.
Startup Visual Studio Code and open a terminal window by
clicking the Terminal > New Terminal menu. Navigate to the
folder where you normally place your development projects. Figure 2: Add required assets to the project

codemag.com Use the MVVM Design Pattern in MVC Core: Part 1 17


Listing 1: The Product class maps the fields in the Product table to properties in this class Save this workspace by clicking on the File > Save Work-
space As… menu. Set the name to MVVMSampleWS and
using System;
using System.ComponentModel.DataAnnotations;
place it into the MVVMSample folder. In the future, you can
using System.ComponentModel.DataAnnotations.Schema; now double-click on this MVVMSampleWS.code-workspace
file to open your solution.
namespace MVVMEntityLayer
{ Add Data Annotations
[Table("Product", Schema ="SalesLT")]
public partial class Product In order to map a class to a table using the Entity Frame-
{ work, you need some data annotation attribute classes.
public int? ProductID { get; set; } These data annotation classes are not a part of the default
public string Name { get; set; } .NET Core library, so you need to add a library. Click on the
public string ProductNumber { get; set; }
public string Color { get; set; } Terminal > New Terminal menu and you should be shown
[DataType(DataType.Currency)] options to open the new terminal window in either the
[Column(TypeName = "decimal(18, 2)")] MVVMSample or the MVVMEntityLayer folder. Select the
public decimal StandardCost { get; set; } MVVMEntityLayer folder and type in the following command
[DataType(DataType.Currency)]
[Column(TypeName = "decimal(18, 2)")]
to add the appropriate DLL so you can use the data annota-
public decimal ListPrice { get; set; } tion attributes.
public string Size { get; set; }
[Column(TypeName = "decimal(8, 2)")] dotnet add package
public decimal? Weight { get; set; }
Microsoft.AspNetCore.Mvc.DataAnnotations
[DataType(DataType.Date)]
public DateTime SellStartDate { get; set; }
[DataType(DataType.Date)] Create the Product Class
public DateTime? SellEndDate { get; set; } Within the MVVMEntityLayer, project rename the Class1.cs
[DataType(DataType.Date)] file to Product.cs. Open the Product.cs file, remove the code
public DateTime? DiscontinuedDate { get; set; }
} currently in the file and replace it with the code in Listing
} 1. Most of the code in this Product class should be familiar
to you. The [Table()] attribute is used to inform the Entity
Framework of the name of the table and schema in which
Try It Out to find the table in the database. Add the [Column()] at-
To ensure that everything is created correctly, click the Run > tribute to any decimal values so the precision and scale can
Start Debugging menu and a Web page should be displayed be specified. Add the [DataType()] attribute to any fields
in your default browser. to be displayed as currency or date values. MVC Core reads
the [DataType()] attribute when using the DisplayFor()
method and if it’s currency, displays the value in the correct
Create the Entity Layer Project format on the Web page.
Now that you have the MVC project created, start building
the rest of your projects beginning with the Entity project. Reference Entity Layer from the MVVM Sample Project
In this project is where you place all your classes that map If you’re used to using Visual Studio, you know that in order
to each table in your database. This project should be a sim- to use classes from another project, you must include a ref-
ple class library with as few references to DLLs as possible. erence to that DLL. It’s no different in VS Code, but to set
If you keep the references to a minimum, you can reuse your the reference, you need to type in a command. Click on the
Entity DLL in other projects. Although you’re going to map Terminal > New Terminal menu and select the MVVMSample
the Product entity in this article to a table in a SQL Server folder. Set a reference to the MVVMEntityLayer project using
database, by keeping your entity classes in a separate DLL, the following command.
you can also map Product data from an XML or JSON file to
the same Product entity class. dotnet add . reference
../MVVMEntityLayer/MVVMEntityLayer.csproj
To create the Entity project, open a terminal window by
clicking on the Terminal > New Terminal menu. Go to your Try It Out
development directory (in my case that was D:\Samples). To ensure that you’ve typed in everything correctly, run
Type in the following commands to create a new folder a build task to compile the projects. Because you have a
named MVVMEntityLayer: reference from the MVVMSample to the MVVMEntityLayer, if
you run a build task on the MVVMSample project, it builds
MD MVVMEntityLayer the MVVMEntityLayer project as well. Select the Terminal >
Run Build Task… menu and select the MVVMSample project.
CD MVVMEntityLayer Watch the output in the terminal window and you should
see that it compiles both projects.
dotnet new classlib

The dotnet new classlib command creates a class library Create the Data Layer Project
project with the minimum number of references to .NET Core The next project to create is the Data Layer project in which
libraries. Now that you’ve created this new project, add it you place all your repository classes. These repository class-
to your Visual Studio Code workspace by clicking the File > es interact with the tables in your database through the
Add Folder to Workspace… menu. Select the MVVMEntity- Entity Framework. Each repository class should implement
Layer folder and click on the Add button. You should see the an interface. Having both a class and an interface allows
MVVMEntityLayer project added to your VS Code workspace. you to use DI to inject the concrete implementation of the

18 Use the MVVM Design Pattern in MVC Core: Part 1 codemag.com


interface. This is advantageous when it comes to testing tions such as a connection string. A single DbSet<Product>
time. You can swap out the concrete implementation, but property is created to hold the collection of products that
the rest of your code stays the same. are read from the SalesLT.Product table.

To create this project, open a terminal by clicking on the Add Product Interface Class
Terminal > New Terminal menu. Go to your development Our repository class needs a single method named Get()
directory (in my case, that was D:\Samples). Type in the used to retrieve all records from the Product table. Before
following commands to create a new folder named MVVM- creating the repository class, add an interface with the con-
DataLayer. tract for this Get() method. Create a new file in the Reposi-
toryInterfaces folder named IProductRepository.cs. Add
MD MVVMDataLayer the code shown in the code snippet below.
CD MVVMDataLayer
using System.Collections.Generic;
dotnet new classlib using MVVMEntityLayer;

Add this new project to your Visual Studio Code workspace, namespace MVVMDataLayer
by clicking the File > Add Folder to Workspace… menu. {
Select the MVVMDataLayer folder and click on the Add but- public interface IProductRepository
ton. You should now see the MVVMDataLayer project added {
to your VS Code workspace. Save your workspace by clicking List<Product> Get();
on the File > Save menu. You can go ahead and delete the }
Class1.cs file, as you won’t be needing that. }

Add Entity Framework Add Product Repository Class


For this article, you’re going to use the Entity Framework for In the RepositoryClasses folder, add a new file named Pro-
the concrete implementation of your repository classes. In ductRepository.cs. Enter the code shown in Listing 3 into
order to use the Entity Framework in the .NET Core applica- the new file you created. This class needs to implement the
tion, you need to add a package to the project. Go back to Get() method from the IProductRepository interface. In ad-
the terminal window that should still be open in the MVVM- dition, a private property of the type AdvWorksDbContext
DataLayer folder. Type in the following command to add the should be added. This private property is set from the con-
Entity Framework to the data layer project. structor of this class. When a ProductRepository class is cre-
ated by the DI service, an instance of the AdvWorksDbCon-
dotnet add package text also created by the DI service is injected into this class.
Microsoft.EntityFrameworkCore.SqlServer
Reference Data Layer from MVVM Sample Project
Add Some Folders Because the DI system is going to create an instance of our
As you’re going to have different types of files in this proj- ProductRespository and the AdvWorksDbContext, you’re go-
ect, add three folders in which to store these different files ing to need the data layer project referenced from the MVC
named Models, RepositoryClasses, and RepositoryInter- Core project. Click on the Terminal > New Terminal menu
faces. In the Models folder, place at least one class that and select the MVVMSample folder. Set a reference to the
inherits from the Entity Framework’s DbContext class. You’re MVVMDataLayer project using the following command.
going to have one repository class for each table that you
need to interact with in your database. Place these classes dotnet add . reference
in the RepositoryClasses folder. Into the RepositoryInter- ../MVVMDataLayer/MVVMDataLayer.csproj
faces folder is where you place the corresponding interface
for each repository class. Try It Out
To ensure that you’ve typed in everything correctly, run a
Reference the Entity Layer build task to compile the projects. Because you now have
Because your data layer will be reading data from the Prod-
uct table, you’re going to need access to the Product class
that you created earlier. Add a reference to the Entity Layer Listing 2: Add a class that inherits from DbContext to access your database tables
project you created earlier by opening a terminal window in using Microsoft.EntityFrameworkCore;
the MVVMDataLayer folder and typing the following com- using MVVMEntityLayer;
mand: namespace MVVMDataLayer
{
dotnet add . reference public partial class AdvWorksDbContext
../MVVMEntityLayer/MVVMEntityLayer.csproj : DbContext
{
public AdvWorksDbContext(
Add a DbContext Class DbContextOptions<AdvWorksDbContext> options)
For your simple article, you’re just going to need a single : base(options)
class to inherit from the Entity Framework’s DbContext class. {
Add a new file in the Models folder named AdvWorksDbCon- }
text.cs. Add the code shown in Listing 2 to this new file. public virtual DbSet<Product>
The AdvWorksDbContext class is a standard implementation Products { get; set; }
of an EF DbContext object. The constructor is passed an in- }
stance of a DbContextOptions object used to pass in any op- }

codemag.com Use the MVVM Design Pattern in MVC Core: Part 1 19


Listing 3: Each repository class implements an interface and receives a DbContext object in its constructor
using System; DbContext = context;
using System.Collections.Generic; }
using System.Linq;
using MVVMEntityLayer; private AdvWorksDbContext
DbContext { get; set; }
namespace MVVMDataLayer
{ public List<Product> Get()
public class ProductRepository {
: IProductRepository return DbContext.Products.ToList();
{ }
public ProductRepository( }
AdvWorksDbContext context) }
{

a reference from the MVVMSample to both the MVVMEntity- to be kept track of, so where do you put it? You don’t want
Layer and the MVVMDataLayer projects, all three projects to add it to your entity class as you then need to add ad-
are built. Select the Terminal > Run Build Task… menu and ditional attributes to mark them as not mapped to the table.
select the MVVMSample project. Watch the output in the ter- Plus, you’re going to need these properties on many differ-
minal window and you should see that it compiles all three ent pages, and you don’t want to copy and paste them from
projects. one entity class to another.

This is where a view model class comes in. A view model


Create View Model Project class holds this additional state data along with an instance
Now that you’ve created the Product entity class and the of your entity class (Figure 3). Later in this article series,
data layer to get a collection of Product objects, you can you’re going to add searching, paging, sorting, and CRUD
build your view model class. So far, you may be thinking, logic to the product Web page. At that time, you’re going to
why do I need a view model class? I have everything I need start adding additional properties.
in my Product and my ProductRepository classes. The answer
is because you want to cut down the amount of code you Create your view model project by opening a terminal win-
need to write in your controller, and you’re going to need dow. Go to your development directory and type in the fol-
additional properties other than what is in your entity class. lowing commands to create a new folder named MVVMView-
ModelLayer.
I’m sure you’ve found that many times on your Web pages,
you need additional properties to keep track of the page MD MVVMViewModelLayer
state. This state is keeping track of what page you’re on CD MVVMViewModelLayer
when paging through a large table. Or what sort column
you’re sorted upon. If you’re in an edit page, you might dotnet new classlib
need to keep track of whether the user requested to do an
add or an edit of the record. All of this additional data needs Now that you’ve created this new project, add it to your Vi-
sual Studio Code workspace, by clicking on the File > Add
Folder to Workspace… menu. Select the MVVMViewModel-
Layer folder and click on the Add button. You should now
see the MVVMViewModelLayer project added to your VS Code
workspace. Save your workspace by clicking on the File >
Save menu.

Reference the Entity and Data Layers


Your view model class needs access to the Product and Pro-
ductRepository classes you created earlier. Add a reference
to the entity layer and data layer projects you created ear-
lier by opening a terminal window in the MVVMViewModel-
Layer folder and typing the following commands:

dotnet add . reference


../MVVMEntityLayer/MVVMEntityLayer.csproj

dotnet add . reference


../MVVMDataLayer/MVVMDataLayer.csproj

Create Product View Model Class


In the view model layer project, rename the Class1.cs file
to ProductViewModel.cs and delete all the code within this
file. Add the code shown in Listing 4 to create your Pro-
Figure 3: A view model provides the properties from the model classes, but also provides ductViewModel class. You can see two constructors on this
additional UI properties. class. You need a parameter-less constructor so MVC can

20 Use the MVVM Design Pattern in MVC Core: Part 1 codemag.com


Listing 4: A view model class helps maintain state, exposes model properties to the UI, and interacts with repository classes
using System;
using System.Collections.Generic; public IProductRepository
using System.Linq; Repository { get; set; }
using MVVMDataLayer; public List<Product> Products { get; set; }
using MVVMEntityLayer;
public void HandleRequest()
namespace MVVMViewModelLayer {
{ LoadProducts();
public class ProductViewModel }
{
/// <summary> protected virtual void LoadProducts()
/// NOTE: You need a parameterless {
/// constructor for post-backs in MVC if(Repository == null) {
/// </summary> throw new ApplicationException(
public ProductViewModel() "Must set the Repository property.");
{ }
} else {
Products = Repository.Get()
public ProductViewModel( .OrderBy(p => p.Name).ToList();
IProductRepository repository) }
{ }
Repository = repository; }
} }

create the class in the POST method of your controller. The output in the terminal window and you should see that it
other constructor is passed an instance of a ProductReposi- compiles all projects.
tory class when the ProductViewModel is created by the DI
system in MVC Core.
Set Up the MVC Project
Two public properties are needed in the view model. One is Now that you have the entity, data, and view model proj-
for the repository object. This allows you to set the reposi- ects created to support retrieving and modifying data in the
tory object in the POST method of your controller. The other Product table, it’s now time to set up the MVC project for us-
property is a list of Product objects that will be loaded with ing these classes. The AdvWorksDbContext class needs to be
all the products returned from the table. It’s from this prop- injected using the DI service, so you need to add a reference
erty that the HTML table is built. to the Entity Framework in your MVC project. Select Termi-
nal > New Terminal and ensure that the terminal prompt is
Two methods are created in this first iteration of your view in your MVVMSample project folder. Type in the following
model. The protected LoadProducts() method is responsible command to add the Entity Framework to your MVC project:
for calling the Get() method on the Repository object and
retrieving the list of product objects. The public HandleRe- dotnet add package
quest() method is called from the controller to retrieve the Microsoft.EntityFrameworkCore.SqlServer
product data. In the future, more logic is added to the Han-
dleRequest() method to perform other functions such as Modify the Startup Class
searching, paging, adding, editing, and deleting of product In the Startup class is where you inject your classes into the
data. The HandleRequest() method should be the only pub- MVC DI service. Open the Startup.cs file and add three us-
lic method exposed from this view model class. By making ing statements at the top. You need one each for your Data
all other methods not visible outside the class, your control- Layer, View Model Layer, and for the Entity Framework.
ler only needs to ever call a single method.
using MVVMDataLayer;
Reference View Model Layer from MVVM Sample Project using MVVMViewModelLayer;
using Microsoft.EntityFrameworkCore;
The DI service requires an instance of the ProductViewModel
class to insert into your control, so reference the view model
Next, create a new method in the Startup class named In-
layer project from your MVC Core project. Click on the Ter-
jectAppServices() as shown in Listing 5. This method is
minal > New Terminal menu and select the MVVMSample
where you inject your application-specific classes. I like
folder. Set a reference to the MVVMViewModelLayer project
creating a separate method so I don’t have an overly-large
using the following command.
ConfigureServices() method. Add a call to the InjectAppSer-
vices() method from within the ConfigureServices() method
dotnet add . reference
as shown in the code snippet below.
../MVVMViewModelLayer/MVVMViewModelLayer.csproj

public void ConfigureServices(


Try It Out
IServiceCollection services)
To ensure that you’ve typed in everything correctly, run a {
build task to compile the projects. Because you have a ref- services.AddControllersWithViews();
erence from the MVVMSample to the MVVMEntityLayer, the
MVVMDataLayer, and the MVVMViewModelLayer projects, all // Inject your application services
four projects will be built. Select the Terminal > Run Build InjectAppServices(services);
Task… menu and select the MVVMSample project. Watch the }

codemag.com Use the MVVM Design Pattern in MVC Core: Part 1 21


Add a Connection String "ConnectionStrings": {
In the InjectAppServices() method, you retrieve a connection "AdvWorksConnectionString":
string from the appsettings.json file. Open the appsettings. "Server=Localhost;
json file and add an appropriate JSON property to store your Database=AdventureWorksLT;
connection string. For formatting purposes in this magazine, Trusted_Connection=True;
I had to break the connection string onto several lines. Your MultipleActiveResultSets=true;
connection string must be on a single line. Application Name=MVVM Sample"
},

Listing 5: Build a separate method to inject your classes into the MVC DI service
private void InjectAppServices(
Create a Product List Page
IServiceCollection services) I hope you agree that you now have a very elegant, reus-
{ able, and testable design with the code you’ve written so
// Get connection string from appsettings.json
string cnn = Configuration[
far. It’s now time to see the fruits of your labor expressed in
"ConnectionStrings:AdvWorksConnectionString"]; a controller of your MVC application. Create a new Product-
Controller.cs file in the Controllers folder and add the code
// Add AdventureWorks DbContext object shown in Listing 6 to this new file.
services.AddDbContext<AdvWorksDbContext>(
options => options.UseSqlServer(cnn));
The MVC DI service injects an instance of the ProductRepository
// Add Classes for Scoped DI and the ProductViewModel classes into the ProductController
services.AddScoped<IProductRepository, class. Two private variables hold each of these instances passed
ProductRepository>();
services.AddScoped<ProductViewModel>(); in. When the user navigates to the Product/Products path in
} the MVC application, the HandleRequest() method is called on
the view model. This method loads the products into the Prod-
ucts property in the view model. The view model is then passed
Listing 6: The controller logic is very simple because of DI and the MVVM design pattern to the Products.cshtml page, which you’re creating next.
using Microsoft.AspNetCore.Mvc; Add a Product Page
using Microsoft.Extensions.Logging;
using MVVMDataLayer; Add a Product folder under Views folder in which to put
using MVVMViewModelLayer; the MVC pages for displaying your product data. Create a
Products.cshtml file in this new Product folder and enter
namespace MVVMSample.Controllers
{
the code shown in the snippet below.
public class ProductController : Controller
{ @model MVVMViewModelLayer.ProductViewModel
private readonly IProductRepository _repo;
private readonly ProductViewModel _viewModel;
@{
public ProductController( ViewData["Title"] = "Products";
IProductRepository repo, }
ProductViewModel vm)
{
_repo = repo; <h1>Products</h1>
_viewModel = vm;
} <partial name="_ProductList" />
public IActionResult Products()
{ Add a Product List Partial Page
// Load products Add a partial page named _ProductList.cshtml in the Product
_viewModel.HandleRequest(); folder. Add the code shown in Listing 7 to this new partial page
file. You’re going to be adding more functionality to the product
return View(_viewModel);
} page as you progress through this series of articles, so it makes
} sense to break your page into multiple partial pages. This is a
} good separation of concerns design pattern for Web pages.

Listing 7: Create a partial page for the display of the Product table
@using MVVMEntityLayer @Html.DisplayFor(m => item.Name)
</td>
<table class="table table-bordered table-hover"> <td>
<thead> @Html.DisplayFor(m => item.ProductNumber)
<tr> </td>
<th>Product Name</th> <td class="text-right">
<th>Product Number</th> @Html.DisplayFor(m => item.StandardCost)
<th class="text-right">Cost</th> </td>
<th class="text-right">Price</th> <td class="text-right">
</tr> @Html.DisplayFor(m => item.ListPrice)
</thead> </td>
<tbody> </tr>
@foreach (Product item in Model.Products) { }
<tr> </tbody>
<td> </table>

22 Use the MVVM Design Pattern in MVC Core: Part 1 codemag.com


Modify Index Page Modify Data Layer to Perform Searching
To call the Product List page, open the Index.cshtml page A Search() method needs to be added to both the interface
in the Views\Home folder and modify the code to look like and to the repository class. Open the IProductRespository.
the snippet shown below. cs file and add a new method declaration for the repository
contract.
@{
ViewData["Title"] = "Home Page"; List<Product> Search(ProductSearch entity);
}

<div class="list-group">
<a asp-action="Products"
asp-controller="Product"
class="list-group-item">
Product List
</a>
</div>

Try It Out
Run the application and click on the Product List link. If
you’ve done everything correctly, you should see the list of
products from the Product table in the AdventureWorksLT
database, as shown in Figure 4.

Searching for Products


To further illustrate how simple using the MVVM design pat-
tern makes your controller code, in this part of the article
you add some HTML and code to search for products, as
shown in Figure 5. Add a new entity class named Product-
Search (see Figure 3) to hold the two properties to search
for. You’re also adding a view model base class that all your
view models are going to inherit from. This base class con-
tains, for now, just a single property named EventCommand.
Figure 4: A product list page using the MVVM design pattern
The Search button you see in Figure 5 uses a data- attribute
to specify the command to send to the view model to process.
Here’s the HTML code you’re going to enter in just a bit.

<button type="button"
data-custom-cmd="search"
class="btn btn-success">
Search
</button>

To get the command and send it to the view model, you’re going
to write a little bit of JavaScript to put the value “search” into
a hidden field on this product page. That hidden field is bound
to the EventCommand property in the view model base class. In
the HandleRequest() method you’re going to check to see if the
EventCommand is filled in with a value, in this case “search”,
and if it is, call a Search() method instead of the LoadProducts()
method you called before to display all products.

Create a Product Search Class


To hold data for searching, create a new class in the Entity
Layer project named ProductSearch.cs and add the code
shown in Listing 8. The reason why you’re creating a new
class instead of just reusing the Product class is that eventu-
ally you’re going to add data validation annotations to the
Product class. You’re not going to want those validations on
these search values. Another reason is that if you wanted
to search on the SellStartDate field, you might need two
properties; BeginDate and EndDate to specify a date range.
You don’t want to add these two extra properties to your
Product class because they don’t map to any field in the
Product table. Figure 5: Add two values to search for on your product list page

codemag.com Use the MVVM Design Pattern in MVC Core: Part 1 23


Next, open the ProductRepository.cs file and add a Search()
method to complete the interface contract you just defined. return ret;
In this Search() method, you add a Where() method to see }
if the Name column in the Product table starts with the
value you entered in the ProductSearch Name property and Add View Model Base Class
the ListPrice column is greater than or equal to the value in Add a new folder in the View Model Layer project named
the ListPrice property. BaseClasses. Within this new folder, create a new file
named ViewModelBase.cs. Add the code shown below to
public List<Product> Search( this new file.
ProductSearch entity)
{ namespace MVVMViewModelLayer {
List<Product> ret; public class ViewModelBase {
public ViewModelBase() {
// Perform Searching EventCommand = string.Empty;
ret = DbContext.Products.Where(p => }
(entity.Name == null ||
p.Name.StartsWith(entity.Name)) && public string EventCommand { get; set; }
p.ListPrice >= entity.ListPrice).ToList();
public virtual void HandleRequest() {}
}
Listing 8: Add a new search class to hold the values to search for in the Product table }
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema; Modify the Product View Model Class
To use the EventCommand property, ensure that all your
namespace MVVMEntityLayer view model classes inherit from this base class. Open the
{
public partial class ProductSearch ProductViewModel.cs file and modify the class declaration
{ as shown below.
[Display(Name = "Product Name")]
public string Name { get; set; } public class ProductViewModel : ViewModelBase
[Display(Name = "List Price Greater Than or Equal to")]
[DataType(DataType.Currency)] Add a new property to this class to hold an instance of the
[Column(TypeName = "decimal(18, 2)")] ProductSearch class by entering the code shown below.
public decimal ListPrice { get; set; }
} public ProductSearch SearchEntity { get; set; }
}

Instantiate a new instance of this ProductSearch class in


both constructor methods on the ProductViewModel class.
Listing 9: Add a partial page for the search area on your product Web page An instance is required if you want the user to enter data
@model MVVMViewModelLayer.ProductViewModel into the search values and have that data post back into the
properties in the ProductSearch object. Be sure to also call
<div class="card"> the base class’ constructor by invoking the base() call on
<div class="card-header bg-primary text-light">
<h5 class="card-title"> each constructor.
Search for Products
</h5> public ProductViewModel() : base()
</div> {
<div class="card-body">
<div class="form-row"> SearchEntity = new ProductSearch();
<div class="form-group col"> }
<label asp-for="SearchEntity.Name"
class="control-label"> public ProductViewModel(
</label>
<input asp-for="SearchEntity.Name" IProductRepository repository) : base()
class="form-control" /> {
</div> Repository = repository;
<div class="form-group col"> SearchEntity = new ProductSearch();
<label asp-for="SearchEntity.ListPrice"
}
class="control-label">
</label>
<input asp-for="SearchEntity.ListPrice" Add a SearchProducts() method to the ProductViewModel
class="form-control" /> class. This method is similar to the LoadProducts() method
</div> you wrote earlier in that you check to ensure that the Repos-
</div>
</div> itory property has been set. You then assign to the Products
<div class="card-footer bg-primary text-light"> property the results of calling the Search() method in the
<button type="button" ProductRepository class.
data-custom-cmd="search"
class="btn btn-success">
Search public virtual void SearchProducts() {
</button> if (Repository == null) {
</div> throw new ApplicationException(
</div> "Must set the Repository property.");

24 Use the MVVM Design Pattern in MVC Core: Part 1 codemag.com


}
SPONSORED SIDEBAR:
else {
®
Products = Repository.Search(SearchEntity) Get .NET Core Help
.OrderBy(p => p.Name).ToList(); for Free
}

Instantly Search
} Looking to create a new
or convert an existing

Terabytes
Because you added a virtual HandleRequest() method in application to .NET Core or
the view model base class, add the override keyword to the ASP.NET Core? Get started
HandleRequest() method declaration in the ProductView- with a FREE hour-long
Model class. CODE Consulting session
to make sure you get
public override void HandleRequest()
started on the right foot.
CODE consultants have
Modify the HandleRequest() method to check the EventCom-
been working with and dtSearch’s document filters
contributing to the .NET support:
mand property to see if there’s a value in it. If there’s no
Core and ASP.NET Core
value, call the LoadProducts() method as before. However, teams since the early pre- • popular file types
if there’s a “search” value in the EventCommand property,
call the SearchProducts() method you just created. The Han-
release builds. Leverage • emails with multilevel
our team’s experience
dleRequest() method should now look like the code snippet and proven track record
attachments
below. to make sure your next • a wide variety of databases
project is a success. No • web data
public override void HandleRequest() { strings. No commitment.
switch (EventCommand.ToLower()) { For more information
case "search": visit www.codemag.com/
SearchProducts(); consulting or email us at Over 25 search options
break; info@codemag.com. including:
default: • efficient multithreaded search
LoadProducts(); • easy multicolor hit-highlighting
break;
• forensics options like credit
}
}
card search

Add Hidden Field Partial Page


Because you added a view model base class that’s going to Developers:
be used for all view models, you should add a partial page
that creates hidden fields for the EventCommand property, • SDKs for Windows, Linux,
and the other properties you’re going to add later in this ar- macOS
ticle series. Add to Views\Shared folder a new partial page • Cross-platform APIs for
named _StandardViewModelHidden.cshtml. Add the code C++, Java and NET with.
shown below to this new file. .NET Standard / NET Core .
@model MVVMViewModelLayer.ViewModelBase • FAQs on faceted search,
granular data classification,
<input type="hidden" asp-for="EventCommand" /> Azure and more
Add Search Partial Page
Just like you create a partial page for the product table,
create a partial page for the search area on your product
page as well. Add a new file under the Product folder named Visit dtSearch.com for
_ProductSearch.cshtml. Into this file add the code shown
in Listing 9. • hundreds of reviews and
case studies
The code in the _ProductSearch.cshtml file creates a Boot-
strap card with the search fields contained within that card.
• fully-functional enterprise
In the footer of this card, add a Search button. It’s on this and developer evaluations
Search button that you add the data- attribute that’s going
to send the command “search” to the view model. The Smart Choice for Text
Modify Products Page
Retrieval® since 1991
When you created the Product page earlier, you had a single
<partial> tag to display the _ProductList.cshtml file. You 1-800-IT-FINDS
now need to add two more partial pages to the Product page.
However, you need to wrap these partial tags into a <form>
www.dtSearch.com
tag so the data in the hidden field and the search fields can

codemag.com Use the MVVM Design Pattern in MVC Core: Part 1 25


Listing 10: JavaScript is used to get the command to send to the view model
@section Scripts // post back to view model
{ $("#EventCommand").val(
<script> $(this).data("custom-cmd"));
$(document).ready(function () {
// Connect to any elements // Submit form
// that have 'data-custom-cmd' $("form").submit();
$("[data-custom-cmd]").on( });
"click", function (event) { });
event.preventDefault(); </script>
}
// Fill in the 'command' to

Getting the Sample Code be posted back to the view model in the POST method of only products that start with the letters “HL” displayed in
the Product controller. Open the Products.cshtml page and the product table.
You can download the sample replace the previous <partial> tag with the code shown in
code for this article by visiting the following snippet.
www.CODEMag.com under Summary
the issue and article, or by <form method="post"> In this article, you got a taste of how to architect an MVC
visiting www.pdsa.com/ <partial name="~/Views/Shared/ Core application with multiple projects. Separating your
downloads. Select “Fairway/
_StandardViewModelHidden.cshtml" /> entity, data, and view model classes into separate projects
PDSA Articles” from
<partial name="_ProductSearch.cshtml" /> provides you with the most flexibility and helps you focus on
the Category drop-down.
Then select “Use the
<partial name="_ProductList" /> each layer as you develop. You also learned how little code
MVVM Design Pattern in </form> you need in your controller. Using a hidden field makes it
MVC Core - Part 1” from easy to communicate commands to your view model. In the
the Item drop-down. Post the Form Using JavaScript next article, you’ll learn how to do sorting and paging using
On the Search button, you set the button’s type attribute to the MVVM design pattern.
button, which means that it won’t post the form back to the
controller. It’s now time to see how you’re going to accom-  Paul D. Sheriff
plish that. At the bottom of the Products page, add a section 
for some JavaScript and enter the code shown in Listing 10.

The JavaScript in Listing 10 uses jQuery $(document).


ready() to look for any HTML element that has the attri-
bute “data-custom-cmd” on it when the page loads. It then
hooks into its “click” function and overrides the default click
event. When the button is clicked upon, the value in the
“data-custom-cmd” attribute is retrieved and placed into
the hidden field named EventCommand. The form is then
submitted, which causes the fields in the search, plus the
EventCommand hidden field to be posted to the controller.

The Post Method


The POST method is created by adding the [HttpPost] at-
tribute above an MVC action method with the same name as
the GET method you used earlier. To this POST method, an
instance of the ProductViewModel is created by MVC and the
data contained in the inputs within the <form> tag is filled
into the corresponding properties. Open the ProductsCon-
troller.cs file and add the POST method shown in the follow-
ing code snippet.

[HttpPost]
public IActionResult Products(
ProductViewModel vm)
{
vm.Repository = _repo;
vm.HandleRequest();

return View(vm);
}

Try It Out
Run the application, enter some data, like “HL” for the
Product Name, in the Search field and click on the Search
button. If you’ve done everything correctly, you should see

26 Use the MVVM Design Pattern in MVC Core: Part 1 codemag.com


codemag.com
ONLINE QUICK ID 2005041

Discovering AWS for .NET Developers


Did you know that Amazon is a member of the .NET Foundation? And that Amazon Web Services (AWS) supports a variety of
.NET platforms such as hosting for ASP.NET Core apps? There are also .NET SDKs to connect to AWS services from your .NET
apps, PowerShell modules and extensions for Visual Studio, VSTS, VS Code, and JetBrains Rider to connect to and interact with

many of AWS’s services. I didn’t know any of this and when Go to aws.amazon.com/freetier to create the account. It
it was being relayed to me for the first time, I was surprised does require a credit card. You’ll initially create what is
and a little embarrassed. Not embarrassed that I wasn’t ex- called a “root account.” That’s like a master account and
pert in any of this but just that I hadn’t thought to look at once that exists, you can create Identity and Access Man-
any of this until now. agement (IAM) users in the root account. AWS strongly rec-
ommends, as a best practice, that you only use IAM users
So where to begin? Well, there are also a host of database to build apps and services, not the root account. When you
engines in AWS and, given my long relationship with Entity create an IAM user, you can associate two access types: pro-
Framework and EF Core, that seemed like a good first stop grammatic and management console access. You don’t need
Julie Lerman for me. What I’ll do in this article is take an existing small to create the IAM account in advance as the AWS Toolkit
thedatafarm.com/blog ASP.NET Core API that uses EF Core, use EF Core migrations for Visual Studio (which I’ll walk you through below) will
twitter.com/julielerman to create a database on AWS, interact with the database, give you some helpful guidance for creating IAM user cre-
and explore secrets management for the database creden- dentials and even importing them into the toolkit. But you
Julie Lerman is a Microsoft do need to have the root account before you start. Here’s a
tials both locally and for a deployed app.
Regional Director, Docker
link to additional information about identities in your AWS
Captain and a long-time
RDS is the acronym for Amazon’s Relational Database Ser- account: https://docs.aws.amazon.com/IAM/latest/User-
Microsoft MVP who now
counts her years as a coder vice. Amazon RDS is a database service that provides access Guide/id.html.
in decades. She makes to several relational databases on AWS: SQL Server, Oracle,
MariaDB, MySQL, and PostgreSQL. It also has a database
her living as a coach and
called Amazon Aurora, which is Amazon’s cloud-native, dis-
Setting Up the AWS Toolkit
consultant to software
teams around the world. tributed relational database that’s MySQL and PostgreSQL with Your Credentials
You can find Julie present- compatible. And Aurora is fast; according to Amazon (aws. The AWS Toolkit for Visual Studio can be installed from the
ing on Entity Framework, amazon.com/rds/aurora): five times faster than access- Visual Studio extensions. There’s also an AWS Toolkit for Vi-
Domain Driven Design and ing MySQL directly through RDS and three times faster sual Studio Code (https://aws.amazon.com/blogs/develop-
other topics at user groups than PostgreSQL. With the exception of Aurora and Aurora er/announcing-aws-toolkit-for-visual-studio-code/), which
and conferences around Serverless (aws.amazon.com/rds/aurora/serverless/), the is focused on creating serverless functions and applications.
the world. Julie blogs at RDS databases are also available via AWS’s free tier, which
thedatafarm.com/blog, makes them great targets for exploring AWS databases with When you first use the Toolkit’s Explorer, a Getting Started
is the author of the highly existing EF Core database providers. page prompts you connect the toolkit to your IAM account
acclaimed “Programming or create an IAM account. Once you’ve associated an IAM
Entity Framework” books, An important concept about RDS databases is that you start account, the explorer lets you see everything tied not just
the MSDN Magazine Data by creating a database instance that’s like a server. When to that particular IAM account but filtered by regions. I’d
Points column and popular creating an instance, you configure it for the type of data- done my first RDS experimentation online, creating a SQL
videos on Pluralsight.com. base you want (e.g., SQL Server or MySQL) along with other Server and a PostgreSQL database, then interacting with
Follow Julie on twitter
attributes that affect performance, scaling, and cost. Then them through Azure Data Studio. This was one of those silly
at julielerman.
you can create databases inside of an instance. More on this exciting moments in the life of a developer when I first got
later. everything connected. Satisfied that I’d worked this out cor-
rectly, I moved on to Visual Studio and the AWS Toolkit. I’ll
I’ll take an existing small ASP.NET Web API and retarget it to walk you through the AWS Toolkit path for creating the IAM
SQL Server Express on RDS to see what the experience is like. account and your first database instance, and then you’ll let
If you want to follow along, you can download the starting EF Core create a database using migrations.
example from github.com/julielerman/codemagawsrds or
on the CODE Magazine page associated with this article. The Toolkit’s Getting Started page (Figure 1) provides a link
to the AWS Console and also lists a small set of steps to per-
form so that you can avoid getting caught up in credential
Setting Up an Account with management. Following these steps, I began by signing into
AWS Free Tier the AWS Console with my root account and then added a
You’ll need an AWS account in order to do any of this work. new IAM user named JulieCodeMag, checking the Program-
And if you don’t have an account, it’s easy to create one matic access type as instructed by the Getting Started page.
using the Free Tier (aws.amazon.com/freetier) which gives The next step is to either add the user to an existing permis-
access to a variety of services that are always free, others sions group or directly to one of the AWS policies. Again, as
that are free for some amount of use (e.g., number of GBs or per the instructions, from the possible power user policies,
hours), others that are free for 12 months, as well as access I chose AdministratorAccess, which is okay for my purposes,
to free trials for some additional services. The RDS services not production. After this, I just left the last few options at
are in the 12 months bucket, giving you up to 750 hours their default and let AWS create the user. Finally, I clicked
a month, 20 GB of storage and 20 GB of backup—certainly the Download .csv button, saved the file and then imported
enough to start exploring. the CSV file into the toolkit using an option on the Getting

28 Discovering AWS for .NET Developers codemag.com


Figure 1: The AWS Toolkit’s Getting Started page before any credentials have been entered

Figure 2: Ensure that Publicly accessible and Add current IP are checked.

Started page. Note that the CSV file doesn’t populate one As a result, the AWS Explorer was then populated with nodes
of the fields—Account Number—and that’s okay. Next, I re- for all of the services that the toolkit provides access to.
named the profile from default to JulieCodeMag and saved And because the JulieCodeMag user has broad access, I was
the credentials. able to see services tied to any of my IAM accounts created

codemag.com Discovering AWS for .NET Developers 29


Figure 3: The DB Instance detail grid

Deploying
in the specific region. I’d created my test database instanc- each instance (see Figure 3) so you can see when the new
You can learn more about es in the US East (Ohio) region so if the explorer is filtered instance (in my case “codemagmicro”) is available. I did ex-
deploying at https://docs. on that region, I’ll see those instances. If I changed the periment with creating various types of database instances
aws.amazon.com/toolkit- explorer region, I wouldn’t see those database instances. from the Toolkit. As I expected, both PostgreSQL and MySQL
for-visual-studio/latest/ instances took less time than SQL Server although the dif-
user-guide/deployment- ference wasn’t significant
beanstalk.html. Creating an RDS Instance Through
the Toolkit Once the instance is available, I can use EF Core migrations
You will need a database instance (remember, this is like against my little sample application to create a database in
a running server) before EF Core can create databases in the instance. You can download the sample or create a new
that instance. And you can create new instances through ASP.NET Core API. As I’m using VS, I’m doing that through
the toolkit, so let’s do that. the create project workflow, not the CLI. I left defaults as
they are: e.g., no authentication and configured for HTTPS.
Right-click on the Instances node under Amazon RDS, then Given that this is using Amazon’s cloud, the model goes back
choose Launch Instance. That means to launch (create) a to Amazon’s roots with authors and books. And keeping it
new instance. You’ll then see a list of the possible engines simple, one author can write multiple books but there are no
for which you can create an instance including various SKUs co-authors, therefore it’s a strict one-to-many relationship.
of Microsoft SQL Server and all of the other RDS options. I’ll
choose SQL Server Express. For those of you who’re used to public class Author
using EF Core’s Windows default, SQL Server LocalDB, for de- {
velopment, LocalDB is a slice of Express (https://docs.micro- public Author()
soft.com/en-us/sql/database-engine/configure-windows/ {
sql-server-express-localdb?view=sql-server-ver15). But it’s Books = new List<Book>();
not a separate option from Express. Once you’ve selected }
the engine, you’ll be prompted to configure the instance to public int AuthorId { get; set; }
specify the database version, memory (“DB Instance class”) public string Name { get; set; }
for which I chose the smallest: micro and storage. You’ll also public List<Book> Books { get; set; }
be prompted to set up a user name and password. The highest }
version available as I’m writing this is SQL Server 2017.
public class Book
The next page of the configuration, Network and Security, {
has a critical setting that you need to enable, which is to public int BookId { get; set; }
make the database publicly available (see Figure 2) for the public int AuthorId { get; set; }
sake of this exploration. Do keep in mind that it’s not a public string Title { get; set; }
best practice for production. Although the Toolkit will be }
able to see the instance, you won’t be able to connect to
it from your code or from any tools like Server Explorer in Starting with ASP.NET Core 3.0, EF Core is no longer included
Visual Studio, SSMS, Azure Data Studio, etc. By default, the in the default dependencies, so you’ll have to add Microsoft.
databases are locked down and only accessible from within EntityFrameworkCore.SqlServer to the project via NuGet.
the VPC that RDS creates to host the instance. Therefore Additionally, because you’ll be using migrations, add the
it’s also necessary to allow a specific IP address (or range) Microsoft.EntityFrameworkCore.Tools package, which gives
to access RDS so that other Visual Studio tools, such as the you the PowerShell version of the migration commands and
SQL Server Object Browser, are able to connect. To do that, the design time logic.
be sure to check the “Add current IP” option also shown in
Figure 2. With EF Core included, I then added a simple DbContext class
called BookContext. Note the constructor that’s needed for
There’s one more page with configuration for backups and ASP.NET Core to use Dependency Injection (DI).
maintenance. By default, RDS backs up the instance im-
mediately after creation and then performs backups daily. public class BookContext:DbContext
For this exploration, I recommend changing that setting to {
“No Automated Backups,” which makes the instance avail- public BookContext
able more quickly. The rest of the defaults are okay for our (DbContextOptions<BookContext>
purposes. The grid for DB Instances shows the status of options) : base(options) { }

30 Discovering AWS for .NET Developers codemag.com


public DbSet<Author> Authors{get;set;}
public DbSet<Book> Books{get;set;}
}

Now it’s time to wire up the API with the database instance
and specify a database to work with. I’ll name mine Books-
Database because I’m a very creative human. More impor-
tantly, I’ll need the server name.

The URI is formed as (with no line breaks): [instance-


name].[AWS Server Name].[region].rds.amazonaws.com.

The DB Instances view gives you two ways to access the


server URI. One is by right-clicking the instance and choos-
ing properties; then, in the properties window, you can see
and copy the Endpoint value. The other is by right-clicking
the instance and choosing Copy Address to Clipboard.

In order to avoid accidentally posting my database cre-


dentials to GitHub, I’ll use ASP.NET Core’s Secret Manager
(https://docs.microsoft.com/en-us/aspnet/core/security/
app-secrets) to protect these credentials during develop-
ment. If you haven’t used that before, right-click on the
project in Solution Explorer and choose Manage User Se-
crets. This opens up a hidden secrets.json file where you can
specify key pairs for your secrets.
Figure 4: The newly created database in Object Explorer
{
"DbPassword": "mysecretpassword",
"DbUser": "theusername"
}

Then, in appsettings.json, I created the connection string


without the user or password attributes. I’ve masked the
server name with asterisks.

"ConnectionStrings": {
"BooksDb" :
"Server=codemagmicro.***.us-east-2
.rds.amazonaws.com,1433;
Database=BookDatabase"
},

Finally, in the startup file where I configure the services to


use DI to spin up the BookContext, I build up the connec-
tion string from the configuration info found in appset-
tings.json and the user and password from the secrets, and
then pass the resulting connection string to the SQL Server
provider’s options. All of this is achieved with the following Figure 5: The locally running controller displaying data from the AWS database
code that gets added to the ConfigureServices method in
Startup.cs:
If you prefer to debug against a local database, you can
var builder = use a SQL Server LocalDb connection in appsettings.Devel-
new SqlConnectionStringBuilder opment.json (which doesn’t require a password). But that
(Configuration would mean putting the full AWS connection string into your
.GetConnectionString("BooksDb")); secrets file and your code won’t need to build the string as
it’s currently doing. To keep this demo simple, I’ll run every-
builder.UserID = thing against the cloud database.
Configuration["DbUser"];
builder.Password =
Configuration["DbPassword"];
Creating the Database with
Services EF Core Migrations
.AddDbContext<BookContext> Now everything is in place to create and execute migrations.
(options => options.UseSqlServer I’ll start by creating a migration file named Initial with the
(builder.ConnectionString)); EF Core command, add-migration initial, in the Package

codemag.com Discovering AWS for .NET Developers 31


Manager Console (PMC) window. Once the migration file is the BooksContext through the ASP.NET Core DI services—EF
created, I’ll call update-database in the PMC to migrate (in Core should be able to create the BooksDatabase database
this case, create) the BooksDatabase database in the co- in the codemagmicro instance on AWS. If there’s something
demagmicro instance. missing, the command will fail and I’ve found that it provides
pretty useful information in its error messages.
If you have all of the pieces in place—the website instance
is publicly available (again for this exploration, not produc-
tion), your IP address is allowed to connect, your IAM account Connecting to the Database
has the correct permissions, and the database connection Because this is a SQL Server database, you have a variety
string (along with its secret user and password) are tied to of options for connecting: SQL Server Management Studio,

Figure 6: Creating the first secret, DbPassword

32 Discovering AWS for .NET Developers codemag.com


Azure Data Studio, and other third-party tools. The tool- 1. Create the secrets in the Parameter Store console on-
kit makes it easy to connect right through the data tools line.
in Visual Studio. First, you’ll add the instance to Server 2. Add some NuGet packages to the solution.
Explorer and from there to the SQL Server Object Browser. 3. Add some configuration code into the program file of
the API.
Here’s how. In the AWS Explorer, right-click the RDS in-
stance and select the Add to Server Explorer option. This The Parameter Store console is an option in the AWS Sys-
launches a Connection Properties window with the server tems Manager service under the Application Management
name and SQL Server Authentication user name already section.
populated. Just fill in the password and connect. The
server should now be listed under Data Connections in the In there, you’ll find a bright orange “Create parameter” but-
Server Explorer. You can see your database in there, but ton and in the details page for the new parameter, you’ll need
I much prefer using SQL Server Object Explorer in Visual to enter four pieces of information, as shown in Figure 6:
Studio. Just right-click on the server in Server Explorer
and choose Browse in SQL Server Object Explorer, and the • Parameter name: a combination of some name you’ll
instance will be added to the Object Explorer for you where use, specific to the app and the name of the param-
you can see the database that migrations just created (see eter. Remember, my first secret is called DbPassword,
Figure 4). so I’ll use /codemagapi/DbPassword as the name.
• Type: an important step that I missed the first time
Although this is enough for me to be satisfied that every- around, be sure to set the type to SecureString. That
thing is working properly, it wouldn’t feel right not to finish encrypts the value for you. SPONSORED SIDEBAR:
up creating and testing out the API. To do so, I’ll need a • KMS Key Source: choose My current account. Leave
controller that interacts with my model. the associated KMS Key ID at its default (alias/aws/ Need Cloud Help?
ssm). CODE Can Help!
The quickest path is using the Add Controller wizard (right- • Value: the password value
click on the Controllers folder to get to it) to create a new Take advantage of a FREE
controller—API Controller with actions, using Entity Frame- That’s enough to finish up with the Create parameter but- hour-long, remote CODE
Consulting session (yes,
work—for the Author class. Now you can run the API to start ton below.
FREE!) to jumpstart your
interacting with it.
organization’s plans to
Follow the same steps to create the DbUser.
develop solutions in the
With the new API running locally, I used the wonderful Rest cloud. Got questions? We’ll
Client extension for Visual Studio Code (https://marketplace. Next, you’ll update the application to read the secrets from do our best to answer
visualstudio.com/items?itemName=humao.rest-client) AWS instead of ASP.NET Core. Once I know that’s working, them! No strings.
to easily send a request to the API. I ran a POST request to I’ll show you how to make the app read from the local se- No commitment.
the URI to add an author using the new controller. This is crets during development and from AWS at runtime. For more information,
just a simpler alternative to other great apps like Postman visit www.codemag.com/
or Fiddler for sending requests. Use whatever you prefer. Begin by adding the Amazon.Extensions.Configuration. consulting or email us at
Here’s the rest call that I executed: SystemsManager NuGet package to your project. This was info@codemag.com.
originally created by community member Ken Hundley and
POST https://localhost:5001/api/authors adopted by the AWS team, who are now the maintainers.
HTTP/1.1
content-type: application/json Next, you’ll add a new section into the appsettings.devel-
{ opment.json file, which you can access by expanding the
"name": "Julie" appsetting.json node in Solution Explorer. This section al-
} lows any AWS extension to use the permissions assigned to
one of the AWS Toolkit profiles you created. I’ve only cre-
Once that data was in the database, I browsed to the con- ated one, JulieCodeMag and it’s using the us-east-2 region.
troller’s default (localhost:5001/api/authors), which re- Therefore, I’ve configured the section as follows:
quests the GET method of the controller and can see that
the API is truly connecting to the AWS database and re- "AWS": {
turning the single author in that table, as you can see in "Region": "us-east-2",
Figure 5. "Profile": "JulieCodeMag"
}

Getting the Secrets on AWS Finally, you’ll tell the application (in program.cs) to load
for the Deployed App the AWS parameters into the ASP.NET Core’s configurations.
So far, the database connection credentials are stored on The new code, the italicized ConfigureAppConfiguration
my computer using ASP.NET Core Secret Manager. When I method in this code snippet, goes in the CreateHostBuilder
deploy the API to the AWS Cloud, it’ll need access to those method in program.cs.
secrets. AWS provides a few ways to store secrets and make
them available to other AWS services. Because I’m relying public static IHostBuilder
on ASP.NET Core’s ability to read parameters, I’ll use the CreateHostBuilder(string[] args) =>
AWS Systems Manager Parameter Store to tuck away my user Host.CreateDefaultBuilder(args)
and password values. .ConfigureAppConfiguration(
(context, builder) =>
There are a few steps involved. {

codemag.com Discovering AWS for .NET Developers 33


builder.AddSystemsManager Final Thoughts
("/codemagapi");
I love the fact that these modern times enable developers to
})
use the tool and platforms they choose and that companies
.ConfigureWebHostDefaults
like Microsoft and Amazon don’t force us to be siloed into a
(webBuilder =>
single organization. Amazon’s investment in the .NET Foun-
{
dation and support for .NET developers is broad-minded.
webBuilder.UseStartup<Startup>();
That’s important to me as a consultant and coach because I
});
never know what stack my next client will be on. Being able
to take all of my skills and investment in .NET development
By default, ASP.NET Core reads configurations from a num-
and the related IDEs and use them on AWS is valuable. And
ber of sources, such as the appsettings files, your system
for me, apparently with some blinders on for a while, it’s a
environment variables, the ASP.NET Core secrets, and more.
revelation. Thanks to the free tier, I also have the ability to
There’s a precedence that if a setting, such as my DbPass-
explore how I can do that. I look forward to broadening my
word, is found in multiple configuration sources, the last
horizons further on AWS.
one read is the one that’s used. Because the AWS configu-
ration is added in after the others, its DbPassword over-
 Julie Lerman
rides the DbPassword read from the ASP.NET Core secrets

file. Because of the AWS setting in the appsettings.develop-
ment.json file, the extension uses the permissions from the
Toolkit’s JulieCodeMag profile to read the AWS Parameters
online. That’s only needed during development. When you
deploy your application to AWS, you’ll need to be sure that
the VPC instance to which the app gets deployed has the
needed permissions to read the parameters. I won’t be cov-
ering app deployment in this article, as I’m focused on the
RDS usage with EF Core.

To really validate that the parameters are being read from


AWS, not the secrets file, you can edit either DbPassword or
DbUser in your ASP.NET Core secrets, which would cause the
app to fail when trying to connect to the database if they’re
the ones being used.

Running the app with these modifications gives me the


same results as earlier when I browsed to the locally hosted
API. The data that’s in the RDS database is output exactly as
it was in Figure 5.

Now that you have confidence that this is working, you may
want to go back to reading from the local secrets while de-
veloping and the AWS parameters in production. To do that,
you can force the app to only load the AWS configurations
when the application environment is production, not devel-
opment.

I’ve updated ConfigureAppConfiguration method to condi-


tionally load the AWS configurations:

.ConfigureAppConfiguration(
(context, builder) =>
{
if (context.HostingEnvironment
.IsProduction())
{
builder.AddSystemsManager
("/codemagapi");
}
})

Even though I’m not walking you through the final bit of
deploying the API, I did publish the API to AWS Elastic
Beanstalk in order to see for myself that everything was
working as expected. See the deployment article noted in
the sidebar to learn more about deploying apps to AWS from
the toolkit.

34 Discovering AWS for .NET Developers codemag.com


READ
CODE MAGAZINE
ON MOBILE!
codemag.com/mobile
ONLINE QUICK ID 2005051

NestJS Step-by-Step: Connecting


NestJS with Angular (Part 4)
In the third part of this series (https://www.codemag.com/Article/2001081/Nest.js-Step-by-Step-Part-3-Users-and-Authentication),
I introduced Passport.js module and implemented User Authentication with NestJS, Passport.js, and JWT to secure the To Do
REST API endpoints. This article covers how you can connect an Angular front-end app with a NestJS back-end API. NestJS is no

different than any other back-end technology in delivering


REST APIs that are accessible by front-end apps including NestJS is no different from any
Angular, React,JS, Vue.JS, and other front-end frameworks.
other back-end technology for
I used Angular CLI to build an Angular v8 app. The app uses building and providing REST APIs.
Bootstrap 4 for the layout and styling. By the end of the
article, you will build something similar to what you see in
Figure 1:
Right after that, the CLI starts generating the files and fold-
Bilal Haidar The application consists of: ers needed and installs the relevant NPM packages.
bhaidar@gmail.com
https://www.bhaidar.dev • A Login component for users to login to the app Now that the application is created, navigate inside the di-
@bhaidar • A Home component to welcome users rectory and make sure it has been generated successfully.
• A Todo Home component acting as a landing page to Run the command:
Bilal Haidar is an
accomplished author, display todo items and Tasks
Microsoft MVP of 10 years, • A Todo Create component allowing the user to create ng serve --open
ASP.NET Insider, and has a new todo list item
been writing for CODE • A Todo List component to list all available todo lists The command starts an internal Webpack Web server to
Magazine since 2007.  in the database serve the application and opens the browser automatically
• A Task Create component allowing the user to create a for you. You should be seeing something similar to Figure 2:
With 15 years of extensive new task under a specific todo list item
experience in Web develop- • A Task List component to list all available tasks under Great! The app is up and running.
ment, Bilal is an expert in a specific todo list item
providing enterprise Web Let’s add the Authentication module to the application.
solutions. You’ll be able to add new todo and task items, as well as
delete existing items. I’ve left editing a todo list item
He works at Consolidated
and task item as an exercise to practice with Angular and Add Auth Library
Contractors Company in
NestJS. The Auth source code hosts all the components and services
Athens, Greece as a full-
stack senior developer. required by the application. Angular CLI offers two options
to isolate your code:
Bilal offers technical
Create Angular App
consultancy for a variety Start by creating a new Angular v8 app using the Angular • An Angular module
of technologies including CLI. Make sure you install the latest version of the Angular • An Angular library
Nest JS, Angular, Vue JS, CLI using the following command:
JavaScript and TypeScript.  With both, the Auth module lives in an isolated and self-
npm install -g @angular/cli contained environment. A library is more like a mini-app
containing a module file, component, service, TypeScript
Now that the Angular CLI is installed, let’s create an appli- config files, and above all, the public-api.ts TypeScript file.
cation by following the steps: The public-api.ts file is used to specify all the library items
that you wish to export and make visible and accessible to
1. Navigate to the folder on your computer that holds the other libraries or modules in the application.
back-end NestJS source code.
2. Side by side, issue the following command to create a For this article, I’ve decided to make use of Angular Libraries.
new Angular app:
Let’s create the Auth Library by issuing the following com-
ng new todo-client mand:

The Angular CLI prompts you with a set of questions to bet- ng generate library auth
ter customize the experience for your own needs and re-
quirements. My answers to those questions are listed here: This command creates a new library named auth inside a
folder located under the root-level projects folder. The An-
• Would you like to add Angular routing? Yes gular CLI places all libraries under this root-level projects
• Which stylesheet format would you like to use? SCSS folder. The Auth library generates an Auth module that

36 NestJS Step-by-Step: Connecting NestJS with Angular (Part 4) codemag.com


Figure 1: Todo Page

Figure 2: Angular CLI home page

codemag.com NestJS Step-by-Step: Connecting NestJS with Angular (Part 4) 37


can be imported by the main application module. Figure 3 The service defines the currentUserSubject as a BehaviorS
shows the content of Auth library that you’ve just created: ubject<ApplicationUser> instance. At any moment of time,
you can reactively query this variable to return the currently
Add Auth Service authenticated user.
The Auth service hosts the logic required to login and logout
a user from the application. Start by generating a new ser- In addition, the service defines a public property named
vice by running the command: currentUserValue to hold the value of the currentUserSub-
ject at any moment of time. Shortly, you will see how to
ng generate service services/auth --project=auth make use of this property inside the Auth Guard.

The command scaffolds a new auth.service.ts file inside the To logout a user, the Auth service defines this function:
services folder inside the auth library. Listing 1 defines the
login() function. logout() {
localStorage.removeItem('currentUser');
The code initiates a POST /auth/login request to the back- this.currentUserSubject.next(null);
end REST API passing over the username and the password }
that the user has entered on the login form. If the back-
end server successfully authenticates the user and returns a The function removes the user details from the LocalStorage
The Source Code
valid accessToken (JWT), the function: and also resets the user details inside the currentUserSubject.
You can find the source code
of this article and the rest • Stores the user details including the JWT inside Lo- Add Auth Guard
of this series in this repo: calStorage The Auth guard decides if a user can access a requested
https://github.com/bhaidar/ • Populates a private currentUserSubject with the user Route. Depending on whether the user is authenticated or
nestjs-todo-app details not, the Auth guard responds accordingly. Start by generat-
ing a new guard by running the following command:

Listing 1: Login method ng generate guard auth --project=auth


login(username: string, password: string) {
return this.http.post<any>('/auth/login', { username, This command prompts you with a question: Which inter-
password }).pipe( faces do you want this new guard to implement?
map(user => {
if (user && user.accessToken) {
localStorage.setItem('currentUser', • CanActivate
JSON.stringify(user)); • CanActivateChild
this.currentUserSubject.next(user); • CanLoad
}
return user;
}) These are three different guard strategies that you can
); implement in Angular depending on the use case. For this
} application, I’ve chosen the CanActivate option.

Listing 2: Auth service


import { Injectable } from '@angular/core';

import {
ActivatedRouteSnapshot,
RouterStateSnapshot,
CanActivate,
Router
} from '@angular/router';
import { AuthService } from './services/auth.service';

@Injectable({
providedIn: 'root'
})
export class AuthGuard implements CanActivate {
constructor(private router: Router,
private authService: AuthService) {}

canActivate(route: ActivatedRouteSnapshot,
state: RouterStateSnapshot) {
const currentUser = this.authService.currentUserValue;
if (currentUser) {
// logged in so return true
return true;
}

this.router.navigate(['/login'], { queryParams: {
returnUrl: state.url } });
return false;
}
}
Figure 3: Auth library

38 NestJS Step-by-Step: Connecting NestJS with Angular (Part 4) codemag.com


Listing 2 defines the Auth Guard. The CanActivate interface You will see shortly how to make use of this guard when you
implementation starts by accessing the AuthService curren- define the application routes.
tUserValue property.
Add Login Component
• If this property holds a valid user, it returns true. This The user logs into the application using a Login page. This
means that you need to allow the user to access the page communicates with the back-end API to authenticate
Route. the user, generate a new JWT and return results to the call-
• Otherwise, redirect the user to the Login page and ing application. Start by generating a new Angular compo-
prevent them from accessing the current Route. nent by running the following command:

Listing 3: Master page


<div class="container my-3"> <div class="form-group">
<div class="row text-center mb-5"> <label for="password">Password</label>
<div class="col-md-12 bg-light p-3"> <input
<h2>Login</h2> type="password"
</div> class="form-control"
</div> id="password"
placeholder="Password"
<div class="row"> formControlName="password"
<div class="col-md-6 offset-md-3"> />
<form [formGroup]="loginForm" (ngSubmit)="onSubmit()" <div *ngIf="submitted && f.password.errors">
autocomplete="off"> <small
<div class="form-group"> *ngIf="f.password.errors.required"
<label for="usernam">Username</label> class="form-text text-muted"
<input >Password is required</small
type="text" >
class="form-control" </div>
id="username" </div>
formControlName="username" <div *ngIf="error" class="my-3">
placeholder="Username" <small class="form-text text-muted">
/> {{ error | json }}</small>
<div *ngIf="submitted && f.username.errors"> </div>
<small <button type="submit"
*ngIf="f.username.errors.required" class="btn btn-primary">Submit</button>
class="form-text text-muted" </form>
>Username is required</small </div>
> </div>
</div> </div>
</div>

Listing 4: Login component


import { Component, OnInit } from '@angular/core'; // get return url from route parameters or default to '/'
import { FormGroup, FormBuilder, Validators } from '@angular/forms'; this.returnUrl =
import { ActivatedRoute, Router } from '@angular/router'; this.route.snapshot.queryParams.returnUrl || '/';
import { first } from 'rxjs/operators'; }
import { AuthService } from '../../services/auth.service';
get f() {
@Component({ return this.loginForm.controls;
templateUrl: 'login.component.html', }
styleUrls: ['login.component.css']
}) onSubmit() {
export class LoginComponent implements OnInit { this.submitted = true;
loginForm: FormGroup;
submitted = false; // stop here if form is invalid
returnUrl: string; if (this.loginForm.invalid) {
error: string; return;
}
constructor(
private formBuilder: FormBuilder, this.authService
private route: ActivatedRoute, .login(this.f.username.value, this.f.password.value)
private router: Router, .pipe(first())
private authService: AuthService .subscribe(
) {} data => {
this.error = '';
ngOnInit() { this.router.navigate([this.returnUrl]);
this.loginForm = this.formBuilder.group({ },
username: ['bhaidar', Validators.required], error => {
password: ['@dF%^hGb03W~', Validators.required] this.error = error;
}); }
);
// reset login status }
this.authService.logout(); }

codemag.com NestJS Step-by-Step: Connecting NestJS with Angular (Part 4) 39


ng generate component components/login • If the back-end successfully validates the credentials
--project=auth --skipTests and authenticates the user, the function then redi-
rects the user back to the returnUrl page.
This command scaffolds a new Login Angular component • Otherwise, the component displays the user-friendly
inside the components folder. In addition, the command errors returned from the back-end API.
declares this component at the Auth Module level.

Listing 3 defines the HTML template of the Login component.


The component defines a form with two fields: username and When the user tries to access a
password. This form is data-bound to the loginForm that you
will define shortly inside the code-behind of this template. In protected page and the Auth Guard
addition, it adds error/validation UI to notify the user in case redirects him to the Login page,
they enter a wrong username format or they miss entering it also appends to the Login page
one of the two fields. The template also adds a Submit button
to allow the user to login to the application. URL a query string named returnUrl
to preserve the original page URL
Listing 4 defines the TypeScript code of the Login component.
that the user was trying to access.
• It starts by creating the loginForm instance and de-
fining the fields under this form. In this case, only two
fields are defined: username and password.
• It then logs out the user just in case there was still an Add JWT Interceptor
active session from previous logins. In part three of this series, I secured the back-end REST API
• Then it stores the returnUrl in a private variable to endpoints with JWT (JSON Web Tokens). This means that for
use later to redirect the user upon a successful login. every request you send to any of the secured API endpoints,
you need to pass over a valid JWT in the request Header so
When the user submits the login form, Angular executes the that the back-end can verify your identity and allow you to
onSubmit() function. This function starts by: continue accessing the API.

• Making sure the form is valid. For this reason, I’ll add and implement an Angular Inter-
• It then calls the AuthService.login() function passing ceptor that will perform a lookup prior to sending any re-
to it the username and password entered by the user. quest to the server. It checks if there is a valid user record
stored in the Auth Service, extracts the token from the re-
cord, and adds a Bearer Authentication Header token onto
Listing 5: JWT Interceptor the request. This is exactly what the back-end REST API
is expecting to properly verify and authenticate the user
import {
HttpInterceptor, request.
HttpRequest,
HttpHandler, Listing 5 defines the JWT Interceptor.
HttpEvent,
HTTP_INTERCEPTORS
} from '@angular/common/http'; • An Angular interceptor implements the HttpInterceptor.
import { Observable } from 'rxjs'; • It implements a single function named
import { Injectable } from '@angular/core'; intercept(request: HttpRequest<any>, next:
import { AuthService } from './auth.service';
HttpHandler): Observable<HttpEvent<any>>.
@Injectable() • The Angular infrastructure passes to the intercept()
export class JwtInterceptor implements HttpInterceptor { function the current HTTP Request and the next han-
constructor(private authService: AuthService) {} dler in the pipeline (could be another interceptor).
intercept( • The function then accesses the currentUserValue
request: HttpRequest<any>, property on the Auth Service.
next: HttpHandler • If the property has a valid user and a valid access to-
): Observable<HttpEvent<any>> { ken, it clones the current request and sets the Autho-
// add authorization header with jwt token if available
const currentUser = this.authService.currentUserValue; rization header to a Bearer {JWT} string.
if (currentUser && currentUser.accessToken) { • Finally, it returns a call to the next.handle(request)
request = request.clone({ so that other handlers in the request pipelines are ex-
setHeaders: { ecuted all the way to reach the back-end REST API.
Authorization: `Bearer ${currentUser.accessToken}`
}
}); The last line in the listing defines an Angular Provider for
} this interceptor. You’ll provide this interceptor in the App
return next.handle(request);
main module. This way, you ensure that any request the ap-
} plication sends to the back-end REST API has an Authoriza-
} tion header set properly.
export const jwtInterceptorProvider = {
provide: HTTP_INTERCEPTORS, Add Error Interceptor
useClass: JwtInterceptor, The back-end REST API returns a 401 Unauthorized response
multi: true when it can’t verify and authenticate a user. It also returns
}; other types of responses including 400 Bad Request, 403

40 NestJS Step-by-Step: Connecting NestJS with Angular (Part 4) codemag.com


Listing 6: Error Interceptor
import { Injectable } from '@angular/core'; if (err.status === 401 && !window.location.href.includes('/login')) {
import { // auto logout if 401 response returned from api
HttpInterceptor, this.authService.logout();
HttpRequest, location.reload();
HttpHandler, }
HttpEvent,
HttpErrorResponse, const error =
HTTP_INTERCEPTORS err.error.error || err.error.message || err.statusText;
} from '@angular/common/http';
import { Observable, throwError } from 'rxjs'; alert(error);
import { catchError } from 'rxjs/operators';
import { AuthService } from './auth.service'; return throwError(error);
})
@Injectable() );
export class ErrorInterceptor implements HttpInterceptor { }
constructor(private authService: AuthService) {} }

intercept( export const errorInterceptorProvider = {


request: HttpRequest<any>, provide: HTTP_INTERCEPTORS,
next: HttpHandler useClass: ErrorInterceptor,
): Observable<HttpEvent<any>> { multi: true
return next.handle(request).pipe( };
catchError((err: HttpErrorResponse) => {

Forbidden and others. The front-end application needs a Listing 7: TypeORM migration
way to capture the “bad” response from the back-end REST
import { MigrationInterface, QueryRunner } from 'typeorm';
API and act.
export class SeedUserRecord1565812987671 implements
For instance, if the back-end REST API returns a 401 Un- MigrationInterface {
authorized response, this means that the application is public async up(queryRunner: QueryRunner):
Promise<any> {}
requesting a secured API endpoint while the user is not
logged in to the application (no Authorization Header) on public async down(queryRunner: QueryRunner):
the request. In this case, the front-end app should redirect Promise<any> {}
the user to the Login page to authenticate themselves with }
the back-end REST API.

Listing 8: Seed User migration


Angular provides an extendible import { MigrationInterface, QueryRunner } from 'typeorm';
import { UserEntity } from '../users/entity/user.entity';
request pipeline that allows you
to define and implement Request export class SeedUserRecord1565812987671 implements
MigrationInterface {
Interceptors that can act on public async up(queryRunner: QueryRunner): Promise<any> {
const userRepo =
a request before sending it over queryRunner.manager.getRepository(UserEntity);
to the back-end server and after const user = userRepo.create({
receiving a response from username: 'bhaidar',
password: '@dF%^hGb03W~',
the same server. email: 'bhaidar@gmail.com',
});

await userRepo.save(user);
}
I’ll add and implement a new Angular Interceptor to handle
the error responses from the back-end REST API properly. // tslint:disable-next-line: no-empty
public async down(queryRunner: QueryRunner):
Listing 6 defines the ErrorInterceptor class. Promise<any> {}
}
• This interceptor hooks into the response payload.
• If the response HTTP Status Code is 401 and the user
is not on the Login page, it’s likely that the user isn’t classes and styles to render a better Popup or Modal
authenticated or a token has expired. In this case, the dialog to show the errors to the user.
interceptor redirects the user back to the Login page. • Finally, the interceptor throws the errors to signal to
• Any other error received from the back-end REST API is the Angular infrastructure that the response was not
properly handled and stored inside a variable named okay.
error.
• The current interceptor simply alerts the error to the The last line in the listing defines an Angular Provider for
user. You can be fancier and use some Bootstrap 4 this interceptor.

codemag.com NestJS Step-by-Step: Connecting NestJS with Angular (Part 4) 41


Seed User Data Let’s replace the content in this file with the one in List-
This article won’t touch on building a registration page for ing 8.
the application. Instead, I’ll create and seed a user record
into the back-end database to test the application. I’ve implemented the up() function as follows:

Navigate to the server folder of this application, where the • The code uses the queryRunner.manager object to
back-end REST API source code exists, and run the following get access to a Repository instance for the UserEntity.
command: • It then creates a new User entity record.
• Finally, it saves the new entity into the database.
ts-node ./node_modules/typeorm/cli.js
-f ormconfig.json migration:create -n To run the migration and seed your database with a new
SeedUserRecord user record, run the following command:

This command uses the TypeORM CLI to create an empty mi- npm-run-all -s -l clean build:server && ts-node
gration file. Listing 7 shows the file contents. ./node_modules/typeorm/cli.js -f ormconfig.json
migration:run
The up() function() runs when the migration is running.
The down() function runs when rolling back a migration. The command cleans any previous builds of the back-end
REST API source code, it then builds and transpiles the Type-
Script code to proper JavaScript code and finally runs the
migration.
It’s very important to use the
queryRunner.manager object as you If this confuses you, refer to part two of this series and read
Angular CLI Docs
about NestJS and databases.
Angular CLI is by far the most
guarantee that this migration will
popular tool used by Angular use the same Transaction instance Build Application Layout
developers to generate and
establish new Angular apps.
that runs all the migrations rather Let’s quickly build the layout of the application and set up
Make sure you read the docs than creating a new sub-transaction. some routes so that you can start testing things like the
here: https://angular.io/cli Login page. Navigate back to the Angular application.

Figure 4: Home page

42 NestJS Step-by-Step: Connecting NestJS with Angular (Part 4) codemag.com


Listing 9: Master page
import { Component, OnInit } from '@angular/core'; </li>
import { AuthService } from 'projects/auth/src/public-api'; <li *ngIf="loggedIn" class="nav-item"
import { Router } from '@angular/router'; routerLinkActive="active">
<a class="nav-link" (click)="logout()"
@Component({ href="">Logout</a>
selector: 'app-master', </li>
template: ` </ul>
<div class="navbar navbar-expand-lg navbar-light bg-light </div>
shadow fixed-top"> </div>
<div class="container"> </div>
<a class="navbar-brand" href="#"> <section class="py-5 mt-5">
<i class="fas fa-tasks"></i>&nbsp;Todozz</a <div class="container">
> <router-outlet></router-outlet>
<button </div>
class="navbar-toggler" </section>
type="button" `
data-toggle="collapse" })
data-target="#navbarResponsive" export class MasterComponent implements OnInit {
aria-controls="navbarResponsive" public loggedIn = false;
aria-expanded="false"
aria-label="Toggle navigation" constructor(
> private readonly authService: AuthService,
<span class="navbar-toggler-icon"></span> private readonly router: Router
</button> ) {}
<div class="collapse navbar-collapse"
id="navbarResponsive"> ngOnInit() {
<ul class="navbar-nav ml-auto"> this.loggedIn = !!this.authService.currentUserValue;
<li class="nav-item" routerLinkActive="active"> }
<a class="nav-link" [routerLink]="['/']">
Home public logout(): void {
</a> this.authService.logout();
</li> this.router.navigate(['/login']);
<li class="nav-item" routerLinkActive="active"> }
<a class="nav-link" [routerLink]="['/todo']">Todo</a> }

Master Component Listing 10: Home page


Generate a new Angular Master component by running this import { Component, OnInit } from '@angular/core';
command:
@Component({
ng generate component shared/master --skipTests selector: 'app-home',
template: `
--inlineTemplate --inlineStyle
<div class="row text-center">
<div class="col-md-12">
The command scaffolds a new MasterComponent class and <h2 class="">Welcome to Todozz App!</h2>
places the HTML template together with the TypeScript code <p>
and CSS styling in a single file. Replace the content of this Here you can manage your Todo Lists in a breeze!
</p>
component with the code in Listing 9. </div>
</div>
The component defines a Bootstrap navigation bar that dis- `
plays the brand name of the application and links to the })
export class HomeComponent implements OnInit {
Home page, Todo page, and a button to log out from the constructor() {}
application. Depending on whether the user is logged-in or
not, the log out button hides or shows. This logic is handled ngOnInit() {}
inside the code of this component. }

Listing 11: Routes


import { NgModule } from '@angular/core'; {
import { Routes, RouterModule } from '@angular/router'; path: '',
import { LoginComponent } from 'projects/auth/src/public-api'; children: [
import { MasterComponent } from './shared/master/master.component'; {
import { HomeComponent } from './shared/home/home.component'; path: 'login',
component: LoginComponent
const routes: Routes = [ }
{ ]
path: '', },
component: MasterComponent, { path: '**', redirectTo: '' }
children: [ ];
{
path: '', @NgModule({
component: HomeComponent imports: [RouterModule.forRoot(routes)],
}, exports: [RouterModule]
] })
}, export class AppRoutingModule {}

codemag.com NestJS Step-by-Step: Connecting NestJS with Angular (Part 4) 43


Listing 12: Routes with Auth guard
import { NgModule } from '@angular/core'; },
import { Routes, RouterModule } from '@angular/router'; {
import { LoginComponent } from path: '',
'projects/auth/src/public-api'; children: [
{
import { MasterComponent } from './shared/master/master.component'; path: 'login',
import { HomeComponent } from './shared/home/home.component'; component: LoginComponent
}
const routes: Routes = [ ]
{ },
path: '', { path: '**', redirectTo: '' }
component: MasterComponent, ];
canActivate: [AuthGuard],
children: [ @NgModule({
{ imports: [RouterModule.forRoot(routes)],
path: '', exports: [RouterModule]
component: HomeComponent })
}, export class AppRoutingModule {}
]

Home Component This routing module defines three main routes:


Let’s also add a Home component to act as a landing page
for the application. Generate the new component by run- • A route to the Home component
ning the following command: • Another route to the Login page
• A catch-all route that redirects the user to the Home
ng generate component shared/home --skipTests – component
inlineTemplate --inlineStyle
Test App
The command scaffolds a new HomeComponent class and Let’s give it shot and run the following command:
places the HTML template together with the TypeScript code
and CSS styling in a single file. Replace the content of this ng serve --open
component with the code in Listing 10. The component is
fairly simple and displays a message to the user. This command builds the Angular app and opens a browser
instance to start browsing the app, as you see in Figure 4.
Add Routing
Now let’s configure the application routing. Navigate to the This is the application layout so far.
/src/app/app-routing.module.ts file and replace the con-
tent of that file with the code in Listing 11. Test Login
Let’s make use of the Auth Guard to test out the Login page.
Replace the content of the /src/app/app-routing.module.
Listing 13: Proxy config file ts file with the content in Listing 12.
{
"/api": { To test the Login page, you need to run the back-end NestJS
"target": "http://localhost:4000", app. Navigate to the server folder where the NestJS app is
"secure": false hosted and run the following commands in two different
},
"/auth": { terminal command windows:
"target": "http://localhost:4000/auth/",
"secure": false, npm run run:services
"pathRewrite": {
"^/auth": ""
} This command starts up the Docker container that is hosting
} the PostgreSQL database for this application.
}
npm run start:dev

Listing 14: AuthService login() action This command starts the NestJS application in development
async login(loginUserDto: LoginUserDto): mode. Back to the front-end application, at the root folder,
Promise<LoginStatus> { create a new proxy.conf.json file. Replace its content with
// find user in db the content in Listing 13.
const user =
await this.usersService.findByLogin(loginUserDto);
Angular uses the proxy configuration settings to redirect
// generate and sign token requests to the back-end and to the correct port where the
const token = this._createToken(user); NestJS app is running.
return {
username: user.username, Now run the application once again by issuing the following
...token, command:
};
}
ng serve --proxy-config proxy.conf.json --open

44 NestJS Step-by-Step: Connecting NestJS with Angular (Part 4) codemag.com


Figure 5: Login page

The command runs the app by using the proxy configuration Let’s go back to the back-end REST API source code and remem-
file. The application detects that you’re not authenticated ber how you’ve implemented the login logic there. When you en-
and logged in so it redirects you to the Login page like that ter your details on the Login page, the application sends a POST
in Figure 5. /auth/login request to the back-end REST API. It also appends
the username and password data into the payload of the request.
You enter your details and hit the Submit button. If the back-
end REST API can successfully verify you, the application redi- On the back-end REST API level, NestJS chooses the corre-
rects you to the Home page once again. sponding Controller and Action to handle this request. In this

Listing 15: Todo Home component


import { Component, OnInit } from '@angular/core'; <h5>Tasks</h5>
</div>
@Component({ </div>
selector: 'lib-todo-home', <div class="row">
template: ` <div class="col-md-12 my-2">
<div class="row my-2"> <router-outlet></router-outlet>
<div class="col-md-6"> </div>
<div class="row"> </div>
<div class="col-md-12 bg-light py-3 text-center"> </div>
<h5>Todo Lists</h5> </div>
</div> `,
</div> styles: [
<div class="row"> `
<div class="col-md-12 my-2"> .border-3 {
border-width: 3px !important;
</div> }
</div> `
</div> ]
<div class="col-md-6"> })
<div class="row"> export class TodoHomeComponent implements OnInit {
<div constructor() {}
class="col-md-12 bg-light py-3 border-left
border-3 border-primary text-center" ngOnInit() {}
> }

codemag.com NestJS Step-by-Step: Connecting NestJS with Angular (Part 4) 45


Listing 16: Todo Create component
import { Component, OnInit, EventEmitter, Output } `
from '@angular/core'; })
export class TodoCreateComponent implements OnInit {
import { DoAction } public todo = '';
from 'projects/app-common/src/public-api';
@Output()
@Component({ public action: EventEmitter<DoAction> = new EventEmitter();
selector: 'lib-todo-create',
template: ` constructor() {}
<div class="row my-2 mb-4">
<div class="col-md-8 offset-md-2"> ngOnInit() {}
<input
[(ngModel)]="todo" public OnEnter() {
(keyup.enter)="OnEnter()" this.action.emit({ type: 'add-todo',
class="form-control" spayload: this.todo });
placeholder="Type a Todo and hit (Enter)" this.todo = '';
/> }
</div> }
</div>

Figure 6: Todo Home page

case, the login action that you’ve defined on theAuthCon- The login action expects an instance of LoginUserDto containing
troller class. It’s responsible to receive all back-end requests to the username and password. The action then delegates its task to
either log in or register a new user. You may refer back to part the login() function that the AuthService defined in Listing 14.
three of this series to learn more about user authentication:
https://codemag.com/Article/2001081/Nest.js-Step-by-Step- This function starts by:
Part-3-Users-and-Authentication.
• Querying the database for a user given its username
@Post('login') • Creating a new signed JWT when the user exists
public async login(@Body() loginUserDto: • Returning an object containing the username and the
LoginUserDto): Promise<LoginStatus> { signed token
return await
this.authService.login(loginUserDto); Then, it’s the role of the AuthController.login action to re-
} turn the response back to the front-end app.

46 NestJS Step-by-Step: Connecting NestJS with Angular (Part 4) codemag.com


Listing 17: Todo List component
import { Component, OnInit, Input, Output, EventEmitter } </ng-template>
from '@angular/core'; `,
styles: [
import { Todo } from '../../models/todo.model'; `
.todos {
import { DoAction } display: flex;
from 'projects/app-common/src/public-api'; justify-content: center;
}
@Component({
selector: 'lib-todo-list', .todos .todo {
template: ` flex-grow: 1;
<div *ngIf="!todos?.length; else show"> flex-shrink: 0;
No todos yet!</div> max-width: 90%;
<ng-template #show> }
<div class="list-group">
<div .todos .action {
*ngFor="let todo of todos; margin-right: 5px;
let i = index; trackBy: trackByFn" }
class="todos" `
> ]
<div class="action"> })
<button export class TodoListComponent implements OnInit {
(click)="doAction(todo)" @Input()
class="btn btn-danger btn-lg" public todos: Todo[];
title="Delete {{ todo?.name }}"
> @Output()
<i class="fa fa-trash"></i> public action: EventEmitter<DoAction> = new EventEmitter();
</button>
</div> constructor() {}
<div class="todo">
<a ngOnInit() {}
href="#"
[routerLink]="['tasks', todo.id]" public trackByFn(index: number, item: Todo) {
routerLinkActive="list-group-item-primary" return index;
class="list-group-item list-group-item-action" }
>
({{ i + 1 }}) {{ todo?.name }} public doAction(todo: Todo): void {
</a> this.action.emit({ type: 'delete-todo', payload: todo });
</div> }
</div> }
</div>

As you know, the JwtInterceptor intercepts the response, The component splits the screen into two sections. The first
extracts the user details including the JWT, and stores the hosts the Todo lists and the second hosts the Tasks under
data inside LocalStorage. The next time the user initiates each Todo list.
a new request to the server, the front-end application re-
trieves the JWT and attaches it to the existing request. Todo Create Component
Generate a new Todo Create component by running the fol-
Let’s add more features and build the Todo module next. lowing command:

ng generate component components/todo-create –


Add Todo Library project=todo --skipTests --inlineStyle –
Let’s scaffold a new library to host the source code for the inlineTemplate
Todo module. Run the following command to generate the
new library: The command above creates a new TodoCreateComponent
inside the components folder in the todo library. Replace
ng generate library todo the content of this file with the content in Listing 16.

This new library introduces a new module hosted inside The component is fairly simple. It defines a Textbox allowing
todo.module.ts. the user to enter a new Todo List name and hit Enter to save
the Todo List in the database.
Todo Home Component
Let’s add the landing component of this library by running The component listens to the Enter key on the Textbox and
the command: then emits an Output action containing the type of add-todo
and a payload of the name of the Todo List itself. It emits this
ng generate component todo-home --project=todo – action to the outside world, that a new Todo List name has
skipTests --inlineStyle --inlineTemplate been entered. You will see shortly how to make use of this
Output action to store the new Todo List in the database.
This command generates the TodoHomeComponent. Re-
place the content of this component with the content in Todo List Component
Listing 15. Navigate to the Todo link on the home page and Generate a new Todo List component by running the follow-
see the results in Figure 6. ing command:

codemag.com NestJS Step-by-Step: Connecting NestJS with Angular (Part 4) 47


ng generate component components/todo-list – The component receives as input an array of all Todo List
project=todo --skipTests --inlineStyle – items to display to the user. It uses Bootstrap 4 list-group
inlineTemplate styles to display all the items.

The command creates a new TodoListComponent inside the The component emits an Output action containing the
components folder in the todo library. Replace the content type of delete-todo and a payload of the Todo List item
of this file with the content in Listing 17. itself. It emits this action to the outside world once the

Listing 18: Todo component


import { Component, OnInit } from '@angular/core'; this.todos$ = this.refresh$.pipe(
switchMap(() => this.todoService.findAll())
import { Observable, BehaviorSubject } );
from 'rxjs'; }

import { switchMap, tap } from 'rxjs/operators'; public doAction({ type, payload }: DoAction): void {
switch (type) {
import { DoAction } case 'add-todo':
from 'projects/app-common/src/public-api'; this.createTodo(payload);
break;
import { Todo } from '../models/todo.model'; case 'delete-todo':
import { TodoService } from '../services/todo.service'; this.deleteTodo(payload);
import { Router } from '@angular/router'; break;
default:
@Component({ console.log('Unknown action type');
selector: 'lib-todo', }
template: ` }
<lib-todo-create (action)="doAction($event)">
</lib-todo-create> private createTodo(todo: string): void {
<lib-todo-list this.todoService
[todos]="todos$ | async" .create({ name: todo })
(action)="doAction($event)" .subscribe(() => this.refresh$.next(''));
></lib-todo-list> }
`
}) private deleteTodo(todo: Todo): void {
export class TodoComponent implements OnInit { if (
public todos$: Observable<Todo[]>; confirm('Are you sure you want to delete this item?')) {
private refresh$ = new BehaviorSubject<any>(''); this.todoService.delete(todo.id).subscribe(() => {
this.refresh$.next('');
constructor( this.router.navigate(['/todo']);
private readonly router: Router, });
private readonly todoService: TodoService }
) {} }
}
ngOnInit() {

Listing 19: Todo service


import { Injectable } from '@angular/core'; public findAll(): Observable<Todo[]> {
import { return this.http.get<Todo[]>(this.baseUrl, httpOptions).pipe(
HttpHeaders, map((results: any) => results.todos),
HttpClient, catchError(this.handleError)
HttpErrorResponse );
} from '@angular/common/http'; }
import { Observable, throwError } from 'rxjs';
import { Todo } from '../models/todo.model'; public delete(id: string): Observable<{}> {
import { catchError, map } from 'rxjs/operators'; const url = `${this.baseUrl}/${id}`;
// DELETE api/todos/42-5c-...
const httpOptions = { return this.http
headers: new HttpHeaders({ .delete(url, httpOptions)
'Content-Type': 'application/json' .pipe(catchError(this.handleError));
}) }
};
private handleError(error: HttpErrorResponse) {
@Injectable({ if (error.error instanceof ErrorEvent) {
providedIn: 'root' console.error('An error occured:', error.error.message);
}) } else {
export class TodoService { console.log(`Backend returned code ${error.status},
private baseUrl = 'api/todos'; // URL to web api body was: ${error.status}`
);
constructor(private readonly http: HttpClient) {} }

public create(todo: Todo): Observable<Todo> { return


return this.http throwError(`Something bad happened; please try again
.post<Todo>(this.baseUrl, todo, httpOptions) later.`);
.pipe(catchError(this.handleError)); }
} }

48 NestJS Step-by-Step: Connecting NestJS with Angular (Part 4) codemag.com


user clicks to delete a single Todo List item. It passes this Todo List item inside the router-outlet allowing the user
over the corresponding list item the user is trying to to view both the TodoList Items and Tasks under the Todo
delete. List item side by side.

Finally, the component places a router-outlet component Todo Component


under the Tasks section. When the user clicks on a single Now let’s generate the TodoComponent that will host both
Todo List item, the application renders the Tasks page for the TodoCreateComponent and TodoListComponent.

Figure 7: Todo Component

Listing 20: Final routes


import { NgModule } from '@angular/core'; children: [
import { Routes, RouterModule } from '@angular/router'; {
path: 'tasks/:id',
import { LoginComponent } component: TaskComponent
from 'projects/auth/src/public-api'; }
]
import { MasterComponent } from './shared/master/master.component'; }
]
import { HomeComponent } from './shared/home/home.component'; },
import { AuthGuard } from 'projects/auth/src/lib/auth.guard'; {
path: '',
import { TodoHomeComponent, TaskComponent } children: [
from 'projects/todo/src/public-api'; {
path: 'login',
const routes: Routes = [ component: LoginComponent
{ }
path: '', ]
component: MasterComponent, },
canActivate: [AuthGuard], { path: '**', redirectTo: '' }
children: [ ];
{
path: '', @NgModule({
component: HomeComponent imports: [RouterModule.forRoot(routes)],
}, exports: [RouterModule]
{ })
path: 'todo', export class AppRoutingModule {}
component: TodoHomeComponent,

codemag.com NestJS Step-by-Step: Connecting NestJS with Angular (Part 4) 49


Figure 8: Todo Home with Tasks page

Listing 21: TodoController create() action Async Pipe. Whenever this observable changes, the Todo-
@Post() ListComponent receives a new fresh array of Todo Items to
@UseGuards(AuthGuard()) display and render to the user.
async create(
@Body() createTodoDto: CreateTodoDto, As a recap, the TodoCreateComponent emits the add-todo
@Req() req: any,
): Promise<TodoDto> { action while the TodoListComponent emits the delete-todo
const user = req.user as UserDto; action. The TodoComponent handles both actions inside the
doAction($event) function in Listing 18.
return await this.todoService.createTodo(user,
createTodoDto);
}
If the action emitted is of type add-todo, the function
calls another function named createTodo() together with
the payload of the action. If the action emitted is of type
delete-todo, the function calls another function named
Proxying a Back-End Server ng generate component components/todo – deleteTodo() together with the payload of the action.
project=todo --skipTests --inlineStyle –
You can read more about inlineTemplate The createTodo() function, in turn, calls the TodoService.
proxying a back-end server create() function, passing to it a Todo model object as fol-
on the Angular website. The command above creates a new TodoComponent inside lows:
(https://angular.io/guide/ the components folder in the todo library. Replace the con-
build#proxying-to-a-back-
tent of this file with the content in Listing 18. private createTodo(todo: string): void {
end-server)
this.todoService
This component listens to the Output actions of both com- .create({ name: todo })
ponents as follows: .subscribe(() => this.refresh$.next('’));
}
<lib-todo-create
(action)="doAction($event)"></lib-todo-create> The deleteTodo() function, in turn, calls the TodoService.
<lib-todo-list [todos]="todos$ | async" delete() function passing to it the ID of the Todo List object:
(action)="doAction($event)"></lib-todo-list>
private deleteTodo(todo: Todo): void {
The TodoComponent defines the todos$ observable that if (confirm(`Are you sure you want to delete
passes it over to the TodoListComponent by means of an this item?`)) {

50 NestJS Step-by-Step: Connecting NestJS with Angular (Part 4) codemag.com


this.todoService.delete(todo.id).subscribe( Now the TodoHomeComponent hosts the TodoComponent
() => { inside it. Let’s add the corresponding routes to the appli-
this.refresh$.next(''); cation so that you can navigate to the Todo components.
this.router.navigate(['/todo']); Open the /src/app/app-routing.module.ts file and replace
}); its content as in Listing 20.
}
} Run the Docker container, back-end REST API, and Angular
app and check the results in Figure 7.
The code navigates the user back to the /todo page after a
successful deletion of a Todo List item. This is directly relat- You can see both TodoCreateComponent and TodoListCom-
ed to the existence of an internal router-view component, ponent rendered on top of each other. Start playing around
as you will see shortly. by adding new Todo List items or deleting existing ones.
Assuming that I’ve implemented the rest of the Task compo-
You define the /todo/src/lib/models/todo.model.ts as: nents, if you click on any of the Todo List items, you should
see the Tasks management screen as in Figure 8.
export interface Todo {
id?: string; You’ve got a new Tasks screen that is similar to that of the
name: string; Todo Lists to add new tasks or delete existing ones.
createdOn?: Date;
} When you enter a new Todo List item and hit the Enter key,
the application sends a POST /api/todos request together
The TodoService in Listing 19 wraps a few calls to the back- with a request payload that contains the Todo List item.
end REST API via the HttpClient class. For instance, the ser- The back-end REST API receives the request and handles it
vice defines the create() function like so: via the TodoController.create action. Listing 21 shows the
body of the TodoController.create action.
public create(todo: Todo): Observable<Todo> {
return this.http You have decorated the create() action with the @
.post<Todo>(this.baseUrl, todo, httpOptions) UserGuards(AuthGuard()) decorator. This decorator en-
.pipe(catchError(this.handleError)); sures that the AuthGuard runs before the code executes
} this action.

The function sends a POST request to the back-end REST The AuthGuard, if you’ve been through part three of this
API, passing along a request payload containing the Todo series, shows you that it will look for a JWT inside the re-
List object and some options. It finally handles any errors quest Authorization header and verify it. Upon a successful
generated out of this call to a dedicated function named verification of the user, the create() action executes and
handleError() defined in the service in Listing 19. returns a response to the front-end app.

The service defines the httpOptions variable as follows: You can find the source code of this article and the rest of
this series in this repo: https://github.com/bhaidar/nestjs-
const httpOptions = { todo-app.
headers: new HttpHeaders({
'Content-Type': 'application/json'
}) Conclusion
}; This is the final part in this series on learning NestJS Step-
by-Step. I’ve introduced a front-end application written in
The remaining components related to Task management Angular to connect to the back-end REST API. You can see
have similar implementation to these components. I’ll leave how easy it is to connect the two. There’s nothing different
it to you to go and check the source code accompanying this from any other front-end application or other technology,
article to see their implementations. like ASP.NET Core or PHP.

Test Todo NestJS is a large framework and covers so much more than I
Back to Listing 15, find the following section inside the can fit into a four-part series. I recommend that you always
TodoHomeComponent: check the rich documentation website they offer, as it is
always up to date and includes further details that could be
<div class="row"> useful while developing and using the NestJS framework.
<div class="col-md-12 my-2">
</div>  Bilal Haidar
</div> 

Replace it with the following:

<div class="row">
<div class="col-md-12 my-
<lib-todo></lib-todo>
</div>
</div>

codemag.com NestJS Step-by-Step: Connecting NestJS with Angular (Part 4) 51


Ciprian Jichici and
Talk to an RD Markus Egger
At a recent DEVintersection conference in Las Vegas, our own Markus Egger sat down
The Microsoft Regional with his fellow Regional Director Ciprian Jichici to talk about machine learning, AI,
Director Program cyber security and malicious attacks, quantum computing, and much more.
The Regional Director Program, or RDs for short,
is a program that allows Microsoft to identify Not much time has gone by since that talk with Ciprian, yet much has changed. A few
influential individuals in an effort to give the days after discussing cyber security, CODE Magazine was the victim of a major cyber-
community-at-large access to these individuals, attack (something we’ll talk about more in upcoming issues of this magazine). Many
and also to provide a point of communication
and feedback into Microsoft. Regional Directors of our readers will have perhaps noticed some interruptions in our business and the
do NOT work for Microsoft (and they aren’t ability to get all magazines out in time. This incident made it all the clearer that security
paid for being part of the RD program), but was a very important and timely topic. The topic of Quantum Computing also ended
they have a formal relationship with Microsoft
that provides them with great insights and up being interesting for an exciting reason. Markus wasn’t able to talk about it at the
connections within Microsoft. time, but FX on Hulu’s new television series “Devs” revolves exactly around this topic,
and CODE Magazine was involved (see our March/April 2020 issue). Then, in far more
The Microsoft Regional Director website
(https://rd.microsoft.com) defines the RD sobering news, the SARS-CoV-2 virus epidemic hit the world. Topics touched on in this
program in the following words: conversation, such as facial identification, have a direct connection to some countries
being able to handle the spread of the epidemic better than others. The virus also
“The Regional Director Program provides Microsoft
leaders with the customer insights and real- ended up cancelling many events (or forcing them to be online-only), including the
world voices it needs to continue empowering Microsoft MVP and RD Summit in Redmond, thus making it all the rarer for RDs to be
developers and IT professionals with the world’s able to meet in person. Nevertheless, this column will continue, as we will find for a way
most innovative and impactful tools, services,
and solutions. Established in 1993, the program to get RDs to talk and for us to listen in. For now, let’s listen in on what was discussed.
consists of 160 of the world’s top technology
visionaries chosen specifically for their proven Markus Egger: Ciprian, good to see you! when it comes to AI, machine learning, and data
cross-platform expertise, community leadership,
Nice to get together every so often. science, is the hype. Hype is probably the one thing
and commitment to business results. You will
that does the most to derail the real purpose of
typically find Regional Directors keynoting at
top industry events, leading community groups
Ciprian Jichici: Yes, good to see you too! data science. Even with large conferences like this
and local initiatives, running technology-focused one, you see a lot of hype being created. And, even
companies, or consulting on and implementing Markus: You’re in a super interesting field. We see worse than that, you see a lot of hype being con-
the latest breakthrough within a multinational each other at the MVP and RD Summit and occa- sumed. People have these tendencies. Sometimes it
corporation.” sions like that, but don’t have that much oppor- feels to me like people need to believe that AI and
tunity to talk other than online, which is why I’m machine learning guard these magical things that
Regional Directors are expected to have deep excited that we get to sit down together here in you can just wave and almost anything you wish will
technical understanding of many of the Microsoft Vegas. You deal with AI, machine learning, data happen. This cannot be further from the truth.
technologies. Not just that, but RDs are expected science, and cyber security, and you have a big
to have an understanding of, and experience passion for those fields. We’re here at DEVintersec- Markus: We discussed this problem before and
with, competing technologies. RDs are also tion in Las Vegas where we are both presenters. people have a lot of half-knowledge. They see some
expected to go beyond technical expertise and Tell me a little bit about your key areas of interest. scenarios working that blow your mind and thus
have considerable business expertise. Many RDs they conclude anything is now possible. I think
present at corporate events, advise governments Ciprian: These days I do a lot of work in the field there’s probably a lot of work left on our end. It’s
and NGOs, and many similar scenarios. of practical machine learning. I also do a lot of us. as presenters, authors, and educators, as well
Feel free to contact any of the Regional Directors work in data science-related projects, mostly, as consultants, who need to set the expectations a
to get access to an RD’s expertise and a well- data cleansing, data engineering, data enrich- little more realistically. And I know you’re doing a
informed opinion that isn’t shaped or influenced ment, and things like that. I feel so fortunate tremendous amount of work in that direction.
by having to go along with Microsoft’s marketing about the place where I am, from a professional
speak. You can contact RDs to get advice and point of view, because this has been my lifelong Ciprian: Yeah, absolutely. That’s absolutely right.
opinions on your projects regarding technical hobby. And 20 years after I started my career, I’m People have this biased idea, that it’s easier to
needs. You can contact them to help analyze at a point where I actually do most of my produc- become a seasoned professional in data science
how technology will influence your business. tive work in the field of either machine learning, than in other fields. And honestly, I can’t think
You can invite RDs to speak to your project data science, or data engineering. of any reason why it would be easier to become a
stakeholders, board of directors, or corporate data scientist then it would be to become a physi-
event. And you can contact your RD for many And, as much as it’s such a great place to be in to- cist, for example. There’s another example that I
more scenarios. day, it also has a lot of, let’s call them “shades of typically like to do, to tell people about this. It
gray.” One of the things that bothers me the most takes perhaps a couple of weeks to learn to drive

52 Talk to an RD: Ciprian Jichici and Markus Egger codemag.com


a car, but acquiring the ability to drive a Formula entist.” And because there’s a huge lack of skill that business, where 5% less waste occurs, that’s
One car or an Indie Car is something that needs in the field and a huge lack of skilled human re- huge! And you can really make that happen in
to be learned from the earliest possible age. It’s sources in the field, you see organizations, you see many cases. But as you say, there is a real degree of
pretty much the same with all fields. Data science customers giving work to these people. Obviously, craftsmanship. First of all, what questions do you
included. It’s not that easy to break into the field. it’s anybody’s right on this planet to do any kind ask? We have a customer in the oil and gas industry
of work they want, and I’m not commenting in any who wanted to know how they could detect early
As a matter of fact, there’s hype around getting into way on that. The problem is that most of these when drill-heads are going to fail when they drill
the field as well. People think the entry barrier for projects have a tendency to become either chal- oil wells. The data scientists and the AI they used
data science is much higher than for other fields. lenged or failed projects. And then the immediate came to the conclusion that the deeper they go,
I don’t believe that. It does take pretty much the kind of repercussion of that is a backfiring from the more likely it is to break. It boggles the mind,
same amount. It’s still around those 10,000 hours business decision-makers. This isn’t just a theoret- how somebody thought that was an interesting an-
of practice that you need to gain a proper level of ical problem. I’ve already been there. I’ve already swer rather than just completely obvious and not
knowledge, a proper level of experience. And where experienced that talking with high-level business useful. So yes, craftsmanship is a big thing there.
you see these the most in data science and machine decision-makers. They say, “Hey, Ciprian, I’ve seen
learning is not necessarily in the pure theory, the you at this conference. I know you are a seasoned Let’s take this a step further. Right now, there’s
pure math, or the statistics behind it. You see it professional, but, uh, don’t get me wrong. We’ve the theoretical level, the data analysis, the data
when people need to make decisions. Because be- been burned already in these types of projects. science, and of course, people get a lot of benefit
lieve it or not, data science is a lot about trial and And, uh, we just, uh, decided to sit this one out from pre-trained models that they can use. So a
error. And there are many, many cases when you to wait a little bit more to see what’s happening.” lot of it is about using what’s there. And then a
simply don’t have all the moving parts, all the hard lot of it is creating custom models. You maintain
figures that you’ll need to make a 100% informed Markus: They expect proper return on investment. your data and clean your data properly. Out of
decision. And that’s where experience kicks in. I mean, the potential return on investment is huge, all those things that you can do, what are the
That’s when those tens of thousands of hours, that right? But you need to make sure you get there. biggest areas you see? At my company, we work
previous experience, becomes very, very important. We worked with a customer that works with per- in computer vision and similar things. But I know
ishable food items. Things like salads going bad you cast a wider net. You even go into quantum
The other thing that I see a lot in the industry is is a big problem in their business. So this is what computing and cyber security, and I know you’re
a phenomenon that I call the “wanna-be data sci- they’re trying to manage. And if you can optimize very passionate about that.

In this feature, we eavesdrop on a conversation between Ciprian Jichici and Markus Egger, both seasoned Regional Directors.
Talk to Ciprian about: Talk to Markus about:
Cloud Computing, Azure, Big Data, Mobile machine learning and AI, ASP.NET, HTML apps
Development, Software Development, IoT, DevOps, (including Angular, Vue.js, etc.), the cloud,
Microservices, Azure, SaaS, PaaS, Data Analytics, Azure, Microservices, Windows applications,
NoSQL, iOS, Android, Visual Studio, ASP.NET, or C#. software strategy, and much more.
You can contact him at ??? You can contact him at markus@eps-software.com

Ciprian Jichici is an IT professional with a business focus living in Timisoara, Markus Egger is not just an RD, but as the founder and publisher of CODE
Romania. His primary executive position is CEO at Genisoft, one of the most Magazine, he’s also directly associated with this publication. In his main
innovative Microsoft partner companies in Romania. He is also vice-president job, he is the President and Chief Software Architect of EPS Software Corp.
of the board of directors at Timisoara International Airport. Previously, he acted (a company better known for its CODE brands such as CODE Consulting,
as Chairman of the Urban Planning Committee at the City Council of Timisoara CODE Training, CODE Staffing, and CODE Magazine). He is also the founder
and as a member of the Directory Council of Timisoara Basketball Club. of other business ventures, such as Tower 48, Wikinome, and others. In his
own words, he spends his time “like most software developers, writing
The sixteen years he spent researching and teaching computer science at the production code” both for consulting and custom app-dev customers,
West University of Timisoara laid the theoretical and practical foundation of his and also for his own companies. He has worked for some of the largest
cloud computing technical focus. Today he spends most of his professional time companies, including many Fortune 500 companies, such as Microsoft.
designing, architecting, and managing the development of cloud computing Markus often takes on the role as a “CTO for hire.”
solutions on Microsoft Azure. An important part of this activity is helping top-
level business decision-makers realize the potential of cloud computing. The
challenges, complexity, and awesomeness of the many technologies he has to
deal with is what drives and motivates him to learn more every day.

codemag.com Talk to an RD: Ciprian Jichici and Markus Egger 53


TALK TO AN RD

Ciprian: Yeah. Well, speaking of all that, you degree of true or false to the statement, but rather the dark side. Obviously, my take is a balance.
could call me a failed physicist. Most probably if find out that, for example, in the western part of You shouldn’t be overexcited about everything
I hadn’t ended up being an IT guy, I would have the United States there were 525 other people who you can do with machine learning and AI. Nei-
ended up somewhere in quantum physics. That’s were saying a very similar thing. Something that’s ther should you be deeply rooted in the dark side.
one of the fields that’s been my passion ever since very close, semantically and linguistically, to the Like “this is going to kill the world,” right? And
I was a child. And it’s amazing to see that that’s statement that it’s raining in Vegas. It might come if you look throughout our history as a human
now becoming a thing in terms of quantum com- as a surprise, but this is the type of thing that a lot race, it’s always been like this. Think about...oh,
puting. Coming back to your question though, at of companies are interested in. They’re interested I don’t know... E = mc2, perhaps. That equation
the present time, I do a lot of work in exploratory because mostly the companies who have a well- brought us nuclear energy, which is still one of
customer behavior analysis. There are some very developed social consciousness are the ones who’re the cleanest types of energy that we know of. But
interesting developments. There are lots and lots really interested in what is being said. What’s the it also brought us the nuclear bomb. So there’s
of companies who have a real need, a measurable perception about them? Yes, the first level is senti- no reason for me to think that something as pow-
need, for these types of solutions. ment analysis, but I believe this is a deeper level, erful—and make no mistake, machine learning
a level that’s also much more difficult to achieve. and AI are very powerful things—but I have no
And by the way, I think you touched a very im- reason to believe that we’ll deal with them in a
portant point earlier, which is still challenging in This is another very practical area in which we radically different way. And you said it very well:
machine learning today. It’s identifying the com- work. We also do work in IoT. As a matter of fact, The power, specifically the computing power that
pelling business case. You have to fight so much in addition to speaking at the conference, I, to- we have today at our fingertips is already being
hype today. You have to fight so many wrong per- gether with my colleagues and some clients, have used to do harm as well, via machine learning. It
ceptions. Then, when it comes to actually building delivered a two-day workshop on combining IoT might come as a surprise to lots of people, but as
a business case that’s based on hard, measurable with machine learning. This is a very interest- of today, we only need maybe 20 to 30 minutes of
returns on investment, which enables the customer ing field and there’s already a lot happening. We somebody’s decent voice recording to use sophis-
to really measure the performance of your data sci- started with pure IoT and the challenges of typical ticated machine learning to make that person say
ence, we see many, many teams and many compa- IoT solutions, but then we move into how these virtually anything that we want. And it’s not only
nies failing at this very point. They are failing when solutions can be improved. How can they benefit with audio, it’s possible also with video.
it comes to providing a compelling business case. from applying machine learning approaches to
the vast amounts of data. Mostly streaming data
We also do a lot of work in natural language pro- that is generated in the world of IoT.
cessing and text analytics. This is another very, I’m not a believer in the
very interesting area that we’re involved in a lot. Markus: To switch gears a little bit, what do you
We do very practical and hands-on types of proj- make of the threats of all of this? A lot of peo-
“Singularity,” that “Terminator
ects, ranging from classification based on natural ple go, “Oh, I know AI is going to take over the Moment.” But I am a believer
language processing and coding, and going all the world.” They’re going to have a Terminator-like in the duality of any kind
way to processing things like media and use data. scenario. I’m not a big believer in that but what I
do worry about is a different angle of all of this. of powerful technology.
Markus: You essentially take in an audio stream, The deep fakes, the derailing of fact-fullness. The
right? You start, not with the written word, but more sophisticated phishing messages people en-
with the audio of a sentiment. Potentially, you do counter. Isn’t there a real issue in attacks of that
transcriptions and then take it from there, or how nature becoming so sophisticated that it’s really Markus: Yes, absolutely. Which is very interest-
do we imagine that? difficult to battle that? And I do wonder if AI can ing, when you think of what’s going on politically
help us battle it as well, but it seems that’s a lot in the world, among other things.
Ciprian: It’s a little more complicated than that more difficult than to come up with the actual
or a little bit more elaborate in the sense that we malicious content. Ciprian: Exactly.
mostly work with text from online written media.
One of the challenges that we’re aiming or plan- Ciprian: Yeah, it is. And by the way, that “Termina- Markus: Or even just the Orwellian scheme, right?
ning to solve is understanding the real structure of tor Moment,” we have a name for that in machine The Chinese say they can identify anyone, within
information out there in the wild. These days you learning and data science. It’s called the “Singu- a second, using face analysis. Anyone within
see a lot of initiatives around classifying news as larity.” It’s that very moment when a machine, or China, that is. And globally, they have the power,
fake or legitimate news, and things like that. I think a set of machines, exceed the capabilities of the even though not the data sources perhaps, to
that’s not too harsh of a term, but I think it’s a little human brain in all aspects. It has been projected identify anyone within two seconds. Those are, in
bit simplistic. The types of projects we’re involved that the singularity would occur sometime around a way, scary things. And I often wonder: Does it
in are the projects where we aren’t looking to pro- 2010 and then it was updated to 2020. Now it’s take more computing power to create deep fakes
vide a measure of quality for what’s been said out been moved to around 2050. I’m not a believer or to detect them?
there in the public. Rather it’s about estimating the in that either. But I’m a believer in the duality of
support for any statement. Think about a simplified any kind of powerful technology or mechanism or Ciprian: Well, that’s the typical kind of a chicken
situation like this. “Markus said, ‘it’s raining in Ve- theory that we—humankind—get access to. Be- and egg problem. The real problem is—and this ap-
gas’.” What we’re trying to do is not assign a certain cause it’s always a game of the bright side and plies to a certain extent to deep fakes—that deep

54 Talk to an RD: Ciprian Jichici and Markus Egger codemag.com


TALK TO AN RD

fakes are not yet so sophisticated as to be extreme- Ciprian: It’s very important because the Microsofts, pany, we use the Custom Vision and Computer Vi-
ly difficult to detect. But they’re getting there. The Facebooks, Amazons, and Googles of the world, I sion APIs. Microsoft has Face ID and things that
same applies where we have adversarial attacks, believe, together with their immense business suc- are really powerful and probably industry-leading
which basically play on the fundamental differ- cess, also have huge responsibility in terms of what by quite a margin. And there are tons of other
ences between the way the human brain works and they deliver to the world. What’s preventing some providers out there. Who else do you work with?
way the machine brain works. No matter what folks of the really powerful stuff that they’re developing Do you use an AWS or any of those things as well?
will tell you, we’re light years away from mimick- from being used in a non-ethical or non-responsi-
ing the real behavior of the human brain. Even ble way? It’s really encouraging to see that at the Ciprian: We use a wide range of technologies. If you
the smartest machine learning model today has a highest levels starting from the board and the CEO look at some of the most important tool sets and
completely different internal way of working than of the company, Microsoft really gets it and really frameworks today, they’re really vendor agnostic.
the human brain. And there are documented ways understands the importance of driving responsible Think about data preparation and data engineering
of attacking that specific difference. You see these, AI and of driving responsibility in this field. workloads, as an example. By a large margin, Spark
for example, in autonomous self-driving cars. There is the place to go. And fortunately, Spark is avail-
are documented ways in which you can take a few able in Microsoft Azure as Azure Databricks. Spark
rectangular stickers, stick them on a road sign, and is available in AWS. Spark is available in the Google
all of a sudden, yield becomes interpreted as “turn Microsoft really gets it and cloud. This is one very interesting place that we’re
right” or “turn left.” We’re not aware of any proven really understands the in, especially in data engineering and in data sci-
mathematical models that would guarantee success ence through the immense success of open source
in fighting against these things. importance of responsible AI. platforms and various technologies. I’d dare to say
that you have almost a common denominator and
We’re quite far from being clear on whether we then it obviously boils down to either personal pref-
can be effective in dealing with these things. Markus: Some of it always seems to me isn’t erence and perception on whether to use Microsoft
And, as it happened many times in history, even- even just that super high-level. One of the things or AWS or Google. Or it was down to things such as
tually the solution isn’t in the theory and isn’t that’s probably easier to do than a lot of other perceiving Microsoft as being more ethical than one
in the technical part. The solution is in educat- things is to mine the data of people and figure of its competitors. Or it boils down to the sheer cost
ing people to be responsible about these tech- out who the people are that are most easily de- of the platform. So yes, we do a lot of work with
nologies. The solution is in creating a worldwide frauded, and what’s a group of people I can get other vendors as well. It’s true. But most of our work
framework that will help govern the way in which through to, to reach a tipping point. I think if I and most of our workloads run on Microsoft Azure.
these technologies are used. Again, it might seem remember correctly in one of your presentations,
a little bit of a stretch, but it’s a great comparison you talk about Brexit and how a certain subgroup Markus: Have you done anything with any of the
with what happened with nuclear weapons. One of people was identified that was influenced, Asian vendors?
of the reasons why they weren’t used, except for which was enough to sway the vote toward leav-
testing, and except for the end of World War II, ing the EU, rather than remaining. Ciprian: Not yet, no. No. Haven’t had the chance
was because of these frameworks that were cre- yet. To be very honest with you, I’m a firm believer
ated around the world. Ciprian: Yeah. That was one of the cases where in competition and competitive markets. And in
these types of misuse of technology happened way the world of data science, I fear monopoly. This is
You already see this starting. You already see big before the world had the chance to organize itself why I’m so happy that I see, for example, PyTorch
companies, like, for example, Microsoft is really against that type of abuse. We all know the story of slowly but steadily starting to balance TensorFlow.
driving concepts like responsible AI. You see in- Cambridge Analytica, and how social media was lit- Because I don’t want to live in a world where the
dependent bodies doing the same. For example, erally exploited by certain individuals. And as bad only option for doing serious deep learning is Ten-
folks who’ve created the declaration of Montreal as this was, it’s also a very important wake up call. sorFlow. I want to have options. And that can only
on Responsible AI. People are starting to feel I believe it has an important role increasing knowl- be good for us as data scientists, but also for the
this threat. And what’s encouraging for me is edge and awareness on what the bad things are field in its entirety.
that they start realizing that the solution is at a that can happen when machine learning is used in
different level. It’s not necessarily at the techni- a non-ethical and non-responsible way. Markus: I couldn’t agree more.
cal level because that’s a chicken and egg prob-
lem. We’ll get better at creating, for example, Markus: Now to switch to something a little more It’s been a pleasure talking to you, as always.
deep fakes, and then we’ll get better at detecting positive and forward looking again: Both of us Unfortunately, we are out of time, because both
them, and then we’ll get better at creating them, use a lot of Microsoft technologies. In my com- of us have to run off to our next presentations.
and so on and so forth. Good luck with your upcoming talk!

Markus: You and I don’t work for Microsoft, but we The Cambridge Analytica Ciprian: This has been an absolutely great conver-
have a relationship with them. It’s nice to see that scandal was a very sation. We’ll do it again, maybe at the upcoming
a company like Microsoft takes the ethics of all of Regional Director and MVP Summit in Redmond!
that very seriously. Microsoft just been voted the
important wake-up call
most ethical company in North America. It’s very for the industry.  Markus Egger
interesting to see that sort of stuff. 

codemag.com Talk to an RD: Ciprian Jichici and Markus Egger 55


ONLINE QUICK ID 2005061

Introduction to SwiftUI
If you’ve been developing for iOS, you’re no doubt familiar with Storyboard, Apple’s visual tool for developing the user interfaces
of iOS applications. And depending on how early you got started in iOS development, you might even be familiar with
Interface Builder and XIBs. Last year, Apple announced SwiftUI at the WWDC 2019. With SwiftUI, Apple aims to update the iOS

development experience to the modern world. Each itera- equalToConstant: 50))


tion of the tools makes it much easier and more efficient for }
developers to create their apps, but with each innovative
tool that comes along, developers have to start learning all When the button is tapped, the IBAction named bnClicked
over again. In this article, I aim to introduce you to SwiftUI, (a delegate method) is fired. Using the IBOutlet named txt-
and like all my articles, I hope to get you jumpstarted in the Text, you reference the UITextField object and set its text
shortest amount of time. Let’s get started! property to a string:

@IBAction func btnClicked(_ sender: Any) {


Wei-Meng Lee What Is SwiftUI txtText.text = "Hello, UIKit!"
weimenglee@learn2develop.net SwiftUI is a declarative programming framework for develop- }
http://www.learn2develop.net ing user interfaces for iOS and macOS applications. To see
@weimenglee how it compares to existing framework, let’s see how user
interfaces are built before SwiftUI was introduced. View as a Function of State
Wei-Meng Lee is a technolo-
gist and founder of Devel- SwiftUI, on the other hand, is a state-driven, declarative
oper Learning Solutions Prior to SwiftUI, most developers used UIKit and Storyboard framework. In SwiftUI, all the above could be implemented
(www.learn2develop.net), (which is still supported by Apple in the current version of Xcode with the following statements:
a technology company spe- (version 11.3.1)). Using UIKit and Storyboard, developers drag
cializing in hands-on train- and drop View controls onto View Controllers and connect them
ing on the latest technolo- to outlets and actions on the View Controller classes. This model
gies. Wei-Meng has many of building UIs is known as Model View Controller (MVC) and
years of training experiences creates a clean separation between the UI and business logic.
and his training courses
place special emphasis The following shows a simple implementation in Storyboard.
on the learning-by-doing Here, a Button and a TextField view have been added to the
approach. His hands-on View Controller in Storyboard and an outlet and an action
approach to learning have been created to connect to them:
programming makes
understanding the subject import UIKit
much easier than read-
ing books, tutorials, and class ViewController: UIViewController {
documentation. His name
regularly appears in online
@IBOutlet weak var txtText: UITextField!
and print publications such
as DevX.com, MobiForge.
@IBAction func btnClicked(_ sender: Any) {
com, and CODE Magazine.
}

To lay out the views, you use auto-layout to position both


views in the middle of the screen (both horizontally and
vertically). To customize the look-and-feel of the TextField,
you can code it in the viewDidLoad() method:

override func viewDidLoad() {


super.viewDidLoad()

txtText.font = UIFont(
name: "AppleSDGothicNeo-Bold",
size: 20)
txtText.layer.cornerRadius = 8.0;
txtText.layer.masksToBounds=true
txtText.layer.borderColor =
UIColor.lightGray.cgColor
txtText.layer.borderWidth = 2.0
txtText.textAlignment =
NSTextAlignment.center
txtText.addConstraint(
txtText.heightAnchor.constraint( Figure 1: The button and text field in SwiftUI

56 Introduction to SwiftUI codemag.com


import SwiftUI .padding(.leading ,10)
.padding(.trailing ,10)
struct ContentView: View { }

@State private var text = "" }


}
var body: some View {
Here, the UI is created declaratively using code, and there’s no
VStack { need for Storyboard in this example. Layouts are now also speci-
Button(action: { fied declaratively using code (the VStack in this example stacks
self.text = "Hello, SwiftUI!" all the views vertically). Delegates are now replaced with closures.
}) {
Text("Button") More importantly, views are now a function of state: The text
.padding(EdgeInsets( displayed by the TextField view is now bound to the state vari-
top: 10, leading: 10, able text. When the button is tapped, you change the value of
bottom: 10, trailing: 10)) the text state variable, which automatically updates the text
} displayed in the TextField view.

TextField("", text: $text) To alter the behaviour of each view, you use modifiers, which
.multilineTextAlignment( are basically functions that you apply to a view or another view
TextAlignment.center) modifier, thereby producing a different version of the original
.padding(15) view. Examples of modifiers in this example are padding(),
.frame(maxWidth: .infinity, overlay(), font(), and so on.
alignment: .center)
.foregroundColor(Color.black) Figure 1 shows the how the UI looks in SwiftUI when the
.font(.custom( button is clicked.
"AppleSDGothicNeo-Bold",
size: 20.0))
.overlay( Getting the Tools
RoundedRectangle( To start developing using SwiftUI, you need the following:
cornerRadius: 8)
.stroke(Color.gray, • Xcode version 11 or later
lineWidth: 2) • A deployment target (simulator or real device) of iOS
) 13 or later

Figure 2: Launch Xcode and create a new project

codemag.com Introduction to SwiftUI 57


Figure 3: Create a new Single View App project

Figure 4: Name the projectç

58 Introduction to SwiftUI codemag.com


Figure 5: The ContentView.swift file contains the main UI for your app.

• macOS Mojave (10.14) or later (if you are running ma-


cOS Mojave, you can still use SwiftUI but you won’t be
Automatic Previewing of Your UI
able to use live preview and design canvas features; Using the Canvas
full features are only available in macOS Catalina By default, you should see the Inspector window on the
(10.15) and later) right side of the Xcode window. For building your UI using
SwiftUI, you usually don’t need the Inspector window, so
you can dismiss it to gain more screen estate for previewing
Hello, SwiftUI your UI using the Canvas. To dismiss the Inspector window,
Once you’ve installed Xcode, I know you’re very eager to try click on the button on the top right corner of Xcode (see
out SwiftUI. So let’s have a dive in into SwiftUI and see how Figure 6).
it works first-hand.
With the Inspector window dismissed, you should now
Launch Xcode. Click on the “Create a new Xcode project” to see the Canvas on the right-side of Xcode (Figure 7).
create a new project (see Figure 2).

Select Single View App and click Next (see Figure 3).
In Xcode, if you don’t see the Canvas,
Name the project HelloSwiftUI and select the various op- you can bring it up again through
tions, as shown in Figure 4. For the User Interface option,
ensure that SwiftUI is selected. Click Next and save the the Editor > Canvas menu. To
project to a location on your Mac. preview your UI, click the Resume
You should see the project created for you (see Figure 5).
button on the Canvas. You should
The ContentView.swift file contains the user interface for now be able to see the preview.
your application’s main screen.

codemag.com Introduction to SwiftUI 59


Figure 6: Dismissing the Inspector window

Figure 7: Viewing the Canvas on Xcode

60 Introduction to SwiftUI codemag.com


The Canvas lets you preview the UI of your application with- If you now change the color of the Text view (within the
out needing to run the application on the iPhone Simulator Button view) to red, you should see the changes appear
or real device. automatically reflected in the preview:

You can click on the Resume button to start the preview Button(action: {
(see Figure 8). self.text = "Hello, SwiftUI!"
}) {
Let’s now modify the ContentView.swift file with the code Text("Button")
that you’ve seen earlier (see Figure 9). .padding(EdgeInsets(
top: 10, leading: 10,
bottom: 10, trailing: 10))
.foregroundColor(.red)
If you don’t see the Resume button }
when trying to preview your SwiftUI
Note that the automatic update feature of Preview doesn’t
UI, make sure you are running always work. There are times where you have to click Try
macOS Catalina (10.15) or later. Again button to rebuild the preview (see Figure 11).

Live Preview
You may notice that the automatic preview has paused. This If you recall, the code changes the text on the TextField
sometimes happens when the file you’re previewing has when the button is clicked (or tapped on a real device).
some changes that caused the containing module to be re- However, if you try clicking on the button on the preview
built. When that happens, click the Restore button and you canvas, you can observe that there’s no reaction. This is
should now see the preview again (see Figure 10). because the preview canvas only allows previewing of your

Figure 8: Previewing your UI on the Canvas

codemag.com Introduction to SwiftUI 61


Figure 9: Modifying the ContentView.swift file

UI and doesn’t run your application. To run the applica- device, you can modify the ContentView_Previews struct as
tion, you need to click on the Live Preview button (see follows (see also Figure 14):
Figure 12).
ContentView().previewDevice("iPhone 8")
Once the Live Preview mode is turned on, the background
of the simulator turns dark (see left of Figure 13). You can
now click on the button and the text on the TextField will be Creating a News Reader Application
updated (see right of Figure 13). The best way to learn a new framework or tool is to actually
create an application using it. In this section, you will build
a news reader application to download the news headlines,
Generating Different Previews display them in a List view, and allow the user to tap on a
Notice this block of code at the bottom of ContentView. particular news item to read more about the news.
swift?
Examining the Structure of the News Headline Feed
struct ContentView_Previews: PreviewProvider { For this example, you’ll use the free service provided by
static var previews: some View { News API (https://newsapi.org). This is a JSON-based API
ContentView() that provides you with breaking news headlines and allows
} you to search for articles from over 30,000 news sources
} and blogs. To register for your own API key, go to https://
newsapi.org/register.
The ContentView_Previews struct conforms to the Preview-
Provider protocol. This protocol produces view previews in For your project, you’ll retrieve all the top business head-
Xcode so that you can preview your user interface created lines in the US. The URL looks like this: https://newsapi.
in SwiftUI without needing to explicitly run the application org/v2/top-headlines?country=us&apiKey=<API_Key>.
on the iOS Simulator or real devices. Essentially, it controls
what you see on the Preview canvas. As an example, if you The news headline API returns a JSON string containing
want to preview how your UI will look on an older iPhone 8 the details of the news headlines. You can paste the URL

62 Introduction to SwiftUI codemag.com


Figure 10: Restoring the preview shows the UI again

You’re now ready to start coding. Using Xcode, create a


Single View App project and name it NewsReader.

To extract the items from JSON into structures in Swift, cre-


ate the following structs in ContentView.swift:

import SwiftUI

Figure 11: If the preview fails, click Try Again struct Result: Codable {
var articles: [Article]
}
onto a Web browser and obtain the content. Once the JSON
content is displayed on your browser, copy and paste it struct Article: Codable {
into a JSON validator website, such as http://jsonlint. var url: String
var title: String
com, and you’ll have a good idea of the structure of the
var description: String?
JSON content. Figure 15 shows the structure of a sample var urlToImage: String?
of the JSON content: }

Observe that the value of the articles key is an array of struct ContentView: View {
items each containing the details of each article. In each var body: some View {
article, you want to retrieve the following details: Text("Hello, World!")
}
• title: the title of the news item }
• url: the link containing the details of the news item
• description: a synopsis of the news item By conforming to the Codable protocol, the Result and Figure 12: Turn on Live Preview
• urlToImage: the link containing the image for the ar- Article structs are now able to map Swift Objects to JSON to test your application
ticle data, and vice versa. explicitly running it

codemag.com Introduction to SwiftUI 63


Fetching the JSON String ject’s decode() function to convert the JSON content into
Before you see how to fetch the news headlines from the the Result struct that you’ve defined earlier. Once the con-
Web, let’s define a state variable named articles. This state version is done, you assign the result to the articles state
variable stores all the decoded JSON content and you’ll also variable.
use it to bind to your List view for display:
Defining the View
struct ContentView: View { You can now define the view. Use a List view to display the
list of articles:
private let url =
"https://newsapi.org/v2/" + var body: some View {
"top-headlines?country=" + List(articles, id: \.url) { item in
"us&apiKey=<API_KEY>" VStack(alignment: .leading) {
Text(item.title)
@State private var articles = [Article]() .font(.headline)
Text(item.description ?? "")
var body: some View { .font(.footnote)
Text("Hello, World!") }
} }.onAppear(perform: fetchData)
} }

To fetch the news headlines, you shall define the fetchData() The List view is bound to the articles state variable,
function as shown in Listing 1. and for each row in the List view you use a VStack view
to display the title and description of each article. The
You use the dataTask() method of the URLSession.shared onAppear() modifier to the List view specifies that the
object instance to fetch the news headlines. Once the JSON fetchData() function be called when the List view first
content is downloaded, you use the JSONDecoder() ob- appears.

Figure 13: Turning on Live Preview allows you to test your application without explicitly running it

64 Introduction to SwiftUI codemag.com


Listing 1: Defining the fetchData() function
struct ContentView: View { DispatchQueue.main.async {
private let url = "https://newsapi.org/v2/" + // assign the decoded articles to
"top-headlines?country=us&apiKey=<API_KEY>" // the state variable
self.articles =
@State private var articles = [Article]() decodedResult.articles
}
func fetchData() { return
guard let url = URL(string: url) else { }
print("URL is not valid") }
return print("Error: \(error?.localizedDescription ??
} "Unknown error")")
let request = URLRequest(url: url) }.resume()
URLSession.shared.dataTask(with: request) { }
data, response, error in
if let data = data { // data is Optional, so var body: some View {
// you need to unwrap it Text("Hello, World!")
if let decodedResult = try? }
JSONDecoder().decode( }
Result.self, from: data) {
// decoding is successful

Figure 14: Previewing the UI on an older iPhone 8

codemag.com Introduction to SwiftUI 65


Figure 15: The structure of the JSON content displayed using jsonlint.com Figure 16: Displaying the news headlines using the List view

Figure 17: Adding a package to your Xcode project

66 Introduction to SwiftUI codemag.com


For your reference, the content of the ContentView.swift to display an image for each news headlines. As the saying
file is shown in Listing 2. Figure 16 shows how the app goes, a picture is worth a thousand words. In SwiftUI, you
looks when you run the Live Preview on Xcode. can display images using the Image view. However, one key
problem with the Image view is that it’s only capable of
Displaying Images Remotely displaying local images. That is, images that are bundled
So far, the news headlines are displayed nicely using the locally with the application. If you want to display an image
List view. However, it would be much nicer if you were able that’s located on the Web, you’re out of luck.

Listing 2: The code in ContentView.swift


import SwiftUI // decoding is successful
DispatchQueue.main.async {
struct Result: Codable { // assign the decoded articles to
var articles: [Article] // the state variable
} self.articles =
decodedResult.articles
struct Article: Codable { }
var url: String return
var title: String }
var description: String? }
var urlToImage: String? print("Error: \(error?.localizedDescription
} ?? "Unknown error")")
}.resume()
struct ContentView: View { }
private let url = "https://newsapi.org/v2/top-
headlines?country=us&apiKey=<API_KEY>" var body: some View {
List(articles, id: \.url) { item in
@State private var articles = [Article]() VStack(alignment: .leading) {
Text(item.title)
func fetchData() { .font(.headline)
guard let url = URL(string: url) else { Text(item.description ?? "")
print("URL is not valid") .font(.footnote)
return }
} }.onAppear(perform: fetchData)
let request = URLRequest(url: url) }
URLSession.shared.dataTask(with: request) { }
data, response, error in
if let data = data { // data is Optional, so struct ContentView_Previews: PreviewProvider {
// you need to unwrap it static var previews: some View {
if let decodedResult = try? ContentView()
JSONDecoder().decode( }
Result.self, from: data) { }

Listing 3: Adding the statements to load remote images using the URLImage view
import SwiftUI )!),
import URLImage delay: 0.25,
processors:
struct Result: Codable { [Resize(size:
var articles: [Article] CGSize(width: 100.0,
} height: 100.0),
scale: UIScreen.main.scale)],
struct Article: Codable { content: {
var url: String $0.image
var title: String .resizable()
var description: String? .aspectRatio(contentMode:.fit)
var urlToImage: String? .clipped()
} }
).frame(width: 100.0, height: 100.0)
struct ContentView: View { }
private let url = "https://newsapi.org/v2/top-
headlines?country=us&apiKey=<API_KEY>" VStack(alignment: .leading) {
Text(item.title)
@State private var articles = [Article]() .font(.headline)
Text(item.description ?? "")
func fetchData() { .font(.footnote)
... }
} }.onAppear(perform: fetchData)
}
var body: some View { }
List(articles, id: \.url) { item in
HStack(alignment: .top) { struct ContentView_Previews: PreviewProvider {
URLImage( static var previews: some View {
(( URL(string:item.urlToImage ?? ContentView()
"https://picsum.photos/100") }
?? nil }

codemag.com Introduction to SwiftUI 67


One way to fix this is to create your own Image view to load the urlToImage property). If no image is available, make
images remotely. But there are already solutions devel- use of a sample image provided by this site: https://pic-
oped by others, so you can just make use of one of them. sum.photos/. The URL: https://picsum.photos/100 in-
For this purpose, you’ll use the URLImage view located at dicates to the site to return an image of size 100x100
https://github.com/dmytro-anokhin/url-image. pixels.

To make use of the URLImage view, you need to add its Figure 18 shows the image displayed next to each news
package to your project. You can do so by going to Xcode headline.
and selecting File > Swift Packages > Add Package De-
pendency…. Enter this URL: https://github.com/dmytro- Wrapping the List View in a NavigationView
anokhin/url-image (see Figure 17). Now that you have managed to populate the List view with
the various news headlines, you can wrap the List view in a
Click Next in the current page as well as the next page. Final- NavigationView:
ly, click Finish. The package will now be added to the project.
var body: some View {
With the URLImage package added to the project, add the NavigationView {
following statements in bold to the ContentView.swift file, List(articles, id: \.url) { item in
as shown in Listing 3. ...
}.onAppear(perform: fetchData)
The bold statements add the URLImage view (of size .navigationBarTitle("News Headlines")
100x100) to each row in the List view. You need to check }
whether each news headline contains an image (through }

Figure 18: Displaying image next to each news headline Figure 19: Displaying the navigation bar title

68 Introduction to SwiftUI codemag.com


Listing 4: Adding a new file named NewsView.swift to the project
import SwiftUI
import WebKit struct NewsView: View {
let url: String
struct WebView: UIViewRepresentable {
let request: URLRequest var body: some View {
WebView(request: URLRequest(url:
func makeUIView(context: Context) -> URL(string:url)!))
WKWebView { }
return WKWebView() }
}
struct NewsView_Previews: PreviewProvider {
func updateUIView(_ uiView: WKWebView, static var previews: some View {
context: Context) { NewsView(url:
uiView.load(request) "https://codemag.com/Magazine")
} }
} }

The NavigationView is a view for presenting a stack of views By conforming to this protocol, you need to implement the
representing a visible path in a navigation hierarchy. Figure following methods:
19 shows the List view displayed within a NavigationView
with the navigation bar title set. • makeUIView: creates the view object
• updateUIView: updates the state of the view object
You can make the text in the navigation bar title smaller by
setting its display mode to inline:

.navigationBarTitle("News Headlines",
displayMode: .inline)

Figure 20 shows reduced font size of the navigation bar


title.

Creating the Details Page


When the user taps on a row in the List view, you should
display the content of the news in another page. The details
of the news could be obtained through its url property of
the Article struct.

To display the news using its URL, you need to use a Web
browser. In the current version of SwiftUI, not all the views
are implemented yet, and this includes the implementa-
tion of the Web browser, which is available in the existing
WebKit framework and known as the WebView. What you
need to do now is make use of the WebView in your SwiftUI
application.

To do so, let’s add a new SwiftUI View file to the project and
name it as NewsView.swift. Add the following statements
in bold to the NewsView.swift file, as shown in Listing 4.

The UIViewRepresentable protocol allows you to create


and manage a UIView object in your SwiftUI application.

Figure 20: Reducing the font size of the navigation bar title Figure 21: Previewing the detail page

codemag.com Introduction to SwiftUI 69


Once the WebView is created, you can use it by passing in Once the details page is created, you’re ready to link it with
the URL of the page to load. For the preview, you load the the ContentView. Add the following statements in bold to
CODE Magazine home page, as shown in Figure 21. the ContentView.swift file, as shown in Listing 5.

Figure 22: Tapping on a news item displays the news in more detail

Figure 23: The detail page with an empty space below the navigation bar

70 Introduction to SwiftUI codemag.com


Listing 5: Linking the ContentView to NewsView
var body: some View { $0.image
List(articles, id: \.url) { item in .resizable()
NavigationLink(destination: .aspectRatio(contentMode:.fit)
NewsView(url:item.url) .clipped()
) { }
HStack(alignment: .top) { ).frame(width: 100.0, height: 100.0)
URLImage( }
(( URL(string:item.urlToImage ??
"https://picsum.photos/100") VStack(alignment: .leading) {
?? nil Text(item.title)
)!), .font(.headline)
delay: 0.25, Text(item.description ?? "")
processors: .font(.footnote)
[Resize(size: }
CGSize(width: 100.0, }
height: 100.0), }.onAppear(perform: fetchData)
scale: UIScreen.main.scale)], }
content: { }

WebView(request: URLRequest(url:
URL(string:url)!))
.navigationBarTitle("News Details",
displayMode: .inline)
}
}

Figure 24 shows the details view now showing the content


without the empty space. In addition, the navigation bar
also displays a title.

If you don’t want the title, simply pass in an empty string to


the navigationBarTitle() modifier:

.navigationBarTitle("",
Figure 24: The empty space below the navigation bar is gone displayMode: .inline)

Figure 22 shows how the application works. Tap on a row in Summary


the List view and the details will be loaded on the details page. I hope this article has given you a good idea of the power
of SwiftUI. Although SwiftUI is still in its early days, by the
Want to display the navigation bar title back to large text? time you read this, you shouldn’t be too far off from the
Well, you can set it as follows in the ContentView.swift file: next update of SwiftUI. By then, SwiftUI will have ported
more of the UIKit views and you should be able to develop
var body: some View { your applications entirely using SwiftUI.
NavigationView {
List(articles, id: \.url) { item in  Wei-Meng Lee
NavigationLink(destination: 
NewsView(url:item.url)
) {
...
}.onAppear(perform: fetchData)
.navigationBarTitle("News Headlines")
}
}

However, you’ll soon realize that when you navigate to the


details page, there’s a large empty space below the naviga-
tion bar in the details page (see Figure 23).

To resolve this, you need to set the navigation bar title in


the NewsView.swift to display in inline mode:

struct NewsView: View {


let url: String

var body: some View {

72 Introduction to SwiftUI codemag.com


CODE COMPILERS

(Continued from 74)

On April 4, there are approximately 277K con-


firmed cases and 7,400 deaths. In two weeks, May/Jun 2020
that’s a single order of magnitude increase in Volume 21 Issue 3
confirmed cases and over two orders of magni-
tude increases in deaths. Worldwide on March 21, Group Publisher
there were approximately 304K confirmed cases Markus Egger
and 13K deaths. As of April 4, those statistics are Associate Publisher
now approximately 1.1MM and 60K respectively. Rick Strahl
At a high level, what these numbers confirm is Editor-in-Chief
that the USA is THE coronavirus hotspot in the Rod Paddock
world and New York City is the hotspot in the
Managing Editor
hotspot. Over the same period, New York City Ellen Whitney
jumped from 8K confirmed cases to over 69K.
Never in our lifetimes have we seen the rapid Content Editor
Melanie Spiller
onset of such a pervasive problem. With that in
mind, we need to cut ourselves a lot of slack! Editorial Contributors
Otto Dobretsberger
Jim Duffy
How are you doing? What are you doing? Jeff Etter
Mike Yeager
For many of us, working from home is old hat. Writers In This Issue
Speaking for me, it’s different. To some degree, Markus Egger Bilal Haidar
I’ve lost track of time. I’m busier and I have Wei-Meng Lee Julie Lerman
Sahil Malik John V. Petersen
to keep reminding myself to take breaks. I’m a Paul D. Sheriff
news and political junkie. I need to remind my-
self to tune a lot of that out. The old saying is Technical Reviewers
Markus Egger
“never let a crisis go to waste.” My wife and I Rod Paddock
last weekend (March 28th and 29th) cleaned out
18 years-worth of “stuff” from our attic. We had Production
Franz Wimmer
nothing better to do! With the many closures and King Laurin GmbH
where we are on the curve in the United States, 39057 St. Michael/Eppan, Italy
this new normal will be the status quo for quite
Printing
some time. Keep in contact with friends and loved Fry Communications, Inc.
ones. The new phrase is “social distancing.” That 800 West Church Rd.
doesn’t mean we need to distance socially. Keep Mechanicsburg, PA 17055
up your contacts virtually. Take up a new hobby. Advertising Sales
Dig deeper into something you’ve been dabbling Tammy Ferguson
in. Mentally, our collective mettle is going to be 832-717-4445 ext 26
tammy@codemag.com
stressed. Throughout the day, stop, take a mo-
ment, and take a deep breath. Circulation & Distribution
General Circulation: EPS Software Corp.
Newsstand: The NEWS Group (TNG)
And to those in the healthcare sector, especially Media Solutions
those in the front lines, you deserve every bit of
Subscriptions
thanks and consideration we can give you. It’s Subscription Manager
said that we’re at war and that we’re supposed to Colleen Cade
be on a war footing. Although we’re not getting ccade@codemag.com
leadership from where we should expect, we can
US subscriptions are US $29.99 for one year. Subscriptions
lead by example and do what we can as individu- outside the US are US $49.99. Payments should be made
als. Professionally, that means practicing more in US dollars drawn on a US bank. American Express,
patience with those we work with. Everyone is MasterCard, Visa, and Discover credit cards accepted.
Bill me option is available only for US subscriptions.
feeling it, and everyone deals with it in a differ- Back issues are available. For subscription information,
ent way. e-mail subscriptions@codemag.com.

Subscribe online at
Be well and let’s lookout for each other. We at www.codemag.com
CODE Magazine are here with all of you.
CODE Developer Magazine
6605 Cypresswood Drive, Ste 425, Spring, Texas 77379
 John V. Petersen Phone: 832-717-4445


codemag.com CODA: On Forcing Functions 73


CODA

On Forcing Functions
As I write this column, the entire world is in engaged in a battle against the novel coronavirus named
COVID-19. It will be several weeks before this May/June 2020 issue is in printed and delivered. What’s
in store for us? Where will we be? Which of us will become ill? What will the state of our businesses be?

How will we or our loved ones and friends fare? no choice in the matter. Our industry has always store for us. This situation is worse. Nevertheless,
I don’t have a crystal ball. What I do know for been about adapting or dying. That’s not such society tends to be able to undertake the heavy
certain are the characteristics that will lead to an abstract saying anymore. What’s your source lifting necessary to carry on. Our industry is go-
a chance at prevailing in this battle. Words that code strategy? Is pair programming a core prac- ing to be at the forefront of easing that burden’s
come to mind are sacrifice, unselfishness, ac- tice for your team? Are you disciplined enough to lift. We have an opportunity to provide unprec-
countability, responsibility, discipline, and em- work remotely? How will you overcome and adapt edented solutions to difficult problems. Will you
pathy. Whatever plans were made, they need to to these changed circumstances? be ready to help deliver those solutions? The first
be discarded. step to answering that question is whether, as a
member of a community, are you acting respon-
Perhaps agility has never been more relevant. sibly—which has nothing to do with technology
Whether you are an independent contractor or an The Cone of Uncertainty or skill!
employee, you’ll encounter challenges that are we each are subject
common to some and unique to yourself. Never in Before putting the final period on this install-
many of our lifetimes, have we been confronted to has expanded. ment, you’ll notice the back page has a new oc-
with an issue that so clearly has a path to success cupant. I started writing for CODE Magazine back
that depends on what we all do as individuals. in 2000. Besides taking off for a few years when
Local and national governments can lay out all On source code control, if you’re not using Git, I was in law school and practicing law full time,
the plans it wants. At the end of the day, success you may want to strongly consider its adoption I’ve been in just about every issue. I’m thrilled to
begins and ends with us as individuals. And in because it’s optimized for distributed version be taking on this new assignment. I still plan to
this case, it’s a matter of life and death whether control. Even if you use Git, do you embrace the cover technical content in regular contributions.
that be in the context of a person or a business. pull-request model? Are your developers locally This space is about focusing on the non-technical
integrating work from other developers in their aspects of our industry, whether they be legal,
COVID-19 is the mother of all forcing functions. workstation? Is unit-testing a practice you em- philosophical, political, public policy, etc. I’ve
ploy? These and many more items will need to given this space a new name: CODA. In music, the
Mike Tyson said it best, “Everybody has a plan be carefully considered as we continue to grapple coda is the concluding section. One of my goals
until they get punched in the face.” The philo- with the “new normal.” is that when CODE Magazine focuses on a theme,
sophical roots of that saying go back to Prussian this space will add a different, unique, and to
Field Marshal von Moltke, “No plan survives con- “Technical debt” isn’t just limited to our code. some, a provocative perspective on that theme.
tact with the enemy.” The ancient Chinese curse It can also apply to our individual skill set. The For those of you who know me personally, you
is “May you live in interesting times.” Interesting entire premise for how we work is changing. Even know that I’m not shy about sharing my opinions
times indeed. We must be ready to overcome and if the change isn’t so great for you, that may not on a given topic. I look forward to expanding my
adapt at a moment’s notice. The Cone of Uncer- be the case for others you interact with, which participation in CODE Magazine!
tainty we each are subject to has expanded. We means that whether you realize it or not, it’s go-
each need to be responsible because we have ing to be a big change for us all. It means that
our respective obligations. We each need to be we must continually be in learning mode and we
Time Warp, April 4, 2020:
accountable because we must justify our actions. must always be seeking ways to keep our saws How are you doing?
In another context, what I’m describing is being sharp. In this issue of CODE, as always, continual You’re now going to get some insight into our ed-
a good professional. In principle, that’s never a learning is a hallmark. A crisis quickly reveals iting process. I submitted this article on March
discretionary thing. Now, by hook or crook, we’re weaknesses. How will you respond? The knowl- 21, 2020 for editing. On April 4, 2020, I’m going
going to need to “live the sermon” in practice. edge, skill, and—dare I say—gravitas that we through those edits with our awesome editor Mel-
bring to bear on the effort has never been more anie Spiller! That’s a period of two weeks. What
Chances are likely that in the middle of March, important. happened? For reference, you may want to book-
you received directive to work remotely as part mark the Wikipedia Page: https://en.wikipedia.
of a social-distancing strategy to “flatten the I remember 9/11 as if it were yesterday and I re- org/wiki/Timeline_of_the_2019%E2%80%9320_
curve.” In my opinion, COVID-19 will forever member well talking to my best friend Rod Pad- coronavirus_pandemic#Case_statistics that
change our society and as a result, will change dock (CODE Magazine’s intrepid Editor-In-Chief) keeps track of the Coronavirus statistics. On
the way we work and interact. And with that, on the phone while he was grappling with a com- March 21, there were approximately 24K con-
if you’ve not done so, you need to take time to plete transportation lockdown. The conference firmed cases and 300 deaths in the United States.
review your infrastructure and review your skills he was at came to a grinding halt and for those
on how you go about building software. You have first few hours, nobody knew what fate had in (Continued on page 73)

74 CODA: On Forcing Functions codemag.com


KNOWLEDGE
IS POWER!

Sign up today for a free trial subscription at www.codemag.com/subscribe/DNC3SPECIAL

codemag.com/magazine
832-717-4445 ext. 8 • info@codemag.com
UR
GET YO R
O U
FREE H

TAKE
AN HOUR
ON US!
Does your team lack the technical knowledge or the resources to start new software development projects,
or keep existing projects moving forward? CODE Consulting has top-tier developers available to fill in
the technical skills and manpower gaps to make your projects successful. With in-depth experience in .NET,
.NET Core, web development, Azure, custom apps for iOS and Android and more, CODE Consulting can
get your software project back on track.

Contact us today for a free 1-hour consultation to see how we can help you succeed.

codemag.com/OneHourConsulting
832-717-4445 ext. 9 • info@codemag.com

S-ar putea să vă placă și