Skip to content

Commit

Permalink
feat: add trpc client and trpc-app as example (#371)
Browse files Browse the repository at this point in the history
  • Loading branch information
goetzrobin authored May 4, 2023
1 parent 2419412 commit 9b3382c
Show file tree
Hide file tree
Showing 50 changed files with 1,369 additions and 21 deletions.
2 changes: 2 additions & 0 deletions .github/PULL_REQUEST_TEMPLATE.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ What kind of change does this PR introduce?
- [ ] router
- [ ] platform
- [ ] content
- [ ] nx-plugin
- [ ] trpc

## What is the current behavior?

Expand Down
42 changes: 42 additions & 0 deletions apps/trpc-app/.eslintrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
{
"extends": ["../../.eslintrc.json"],
"ignorePatterns": ["!**/*"],
"overrides": [
{
"files": ["*.ts"],
"extends": [
"plugin:@nx/angular",
"plugin:@angular-eslint/template/process-inline-templates"
],
"rules": {
"@angular-eslint/directive-selector": [
"error",
{
"type": "attribute",
"prefix": "trpcApp",
"style": "camelCase"
}
],
"@angular-eslint/component-selector": [
"error",
{
"type": "element",
"prefix": "trpc-app",
"style": "kebab-case"
}
],
"@angular-eslint/no-host-metadata-property": [
"error",
{
"allowStatic": true
}
]
}
},
{
"files": ["*.html"],
"extends": ["plugin:@nx/angular-template"],
"rules": {}
}
]
}
15 changes: 15 additions & 0 deletions apps/trpc-app/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<!DOCTYPE html>
<html class='dark h-full' lang='en'>
<head>
<meta charset='utf-8' />
<title>SPARTAN - Notes App</title>
<base href='/' />
<meta name='viewport' content='width=device-width, initial-scale=1' />
<link rel='icon' type='image/x-icon' href='/assets/spartan.svg' />
<link rel='stylesheet' href='/src/styles.css' />
</head>
<body class='h-full bg-zinc-900'>
<trpc-app-root></trpc-app-root>
<script type='module' src='/src/main.ts'></script>
</body>
</html>
10 changes: 10 additions & 0 deletions apps/trpc-app/postcss.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
const { join } = require('path');

module.exports = {
plugins: {
tailwindcss: {
config: join(__dirname, 'tailwind.config.js'),
},
autoprefixer: {},
},
};
68 changes: 68 additions & 0 deletions apps/trpc-app/project.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
{
"name": "trpc-app",
"$schema": "../../node_modules/nx/schemas/project-schema.json",
"projectType": "application",
"sourceRoot": "apps/trpc-app/src",
"targets": {
"build": {
"dependsOn": ["^build"],
"executor": "@nx/vite:build",
"outputs": [
"{options.outputPath}",
"dist/apps/trpc-app/.nitro",
"dist/apps/trpc-app/ssr",
"dist/apps/trpc-app/analog"
],
"options": {
"configFile": "apps/trpc-app/vite.config.ts",
"outputPath": "dist/apps/trpc-app/client"
},
"defaultConfiguration": "production",
"configurations": {
"development": {
"mode": "development"
},
"production": {
"sourcemap": false,
"mode": "production"
}
}
},
"serve": {
"executor": "@nx/vite:dev-server",
"defaultConfiguration": "development",
"options": {
"buildTarget": "trpc-app:build",
"port": 4205
},
"configurations": {
"development": {
"buildTarget": "trpc-app:build:development",
"hmr": true
},
"production": {
"buildTarget": "trpc-app:build:production"
}
}
},
"extract-i18n": {
"executor": "@angular-devkit/build-angular:extract-i18n",
"options": {
"browserTarget": "trpc-app:build"
}
},
"lint": {
"executor": "@nx/linter:eslint",
"outputs": ["{options.outputFile}"],
"options": {
"lintFilePatterns": ["apps/trpc-app/**/*.ts", "apps/trpc-app/**/*.html"]
}
},
"test": {
"executor": "@nx/vite:test",
"outputs": ["apps/trpc-app/coverage"]
}
},
"tags": [],
"implicitDependencies": ["trpc"]
}
10 changes: 10 additions & 0 deletions apps/trpc-app/src/app.config.server.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { ApplicationConfig, mergeApplicationConfig } from '@angular/core';
import { provideServerRendering } from '@angular/platform-server';
import { appConfig } from './app.config';
import { provideClientHydration } from '@angular/platform-browser';

const serverConfig: ApplicationConfig = {
providers: [provideServerRendering(), provideClientHydration()],
};

export const config = mergeApplicationConfig(appConfig, serverConfig);
12 changes: 12 additions & 0 deletions apps/trpc-app/src/app.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { ApplicationConfig } from '@angular/core';
import { provideClientHydration } from '@angular/platform-browser';
import { provideFileRouter } from '@analogjs/router';
import { provideTRPCClient } from './trpc-client';

export const appConfig: ApplicationConfig = {
providers: [
provideFileRouter(),
provideClientHydration(),
provideTRPCClient(),
],
};
17 changes: 17 additions & 0 deletions apps/trpc-app/src/app/app.component.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { TestBed } from '@angular/core/testing';
import { RouterTestingModule } from '@angular/router/testing';
import { AppComponent } from './app.component';

describe('AppComponent', () => {
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [RouterTestingModule, AppComponent],
}).compileComponents();
});

it('should create the app', () => {
const fixture = TestBed.createComponent(AppComponent);
const app = fixture.componentInstance;
expect(app).toBeTruthy();
});
});
14 changes: 14 additions & 0 deletions apps/trpc-app/src/app/app.component.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { ChangeDetectionStrategy, Component } from '@angular/core';
import { RouterOutlet } from '@angular/router';

@Component({
selector: 'trpc-app-root',
standalone: true,
imports: [RouterOutlet],
host: {
class: 'max-w-screen-md mx-auto block h-full bg-zinc-900 text-zinc-50',
},
changeDetection: ChangeDetectionStrategy.Default,
template: ` <router-outlet></router-outlet> `,
})
export class AppComponent {}
103 changes: 103 additions & 0 deletions apps/trpc-app/src/app/pages/index.page.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
import { Component } from '@angular/core';
import { injectTRPCClient } from '../../trpc-client';
import { AsyncPipe, DatePipe, JsonPipe, NgFor, NgIf } from '@angular/common';
import { FormsModule, NgForm } from '@angular/forms';
import { waitFor } from '../../wait-for';
import { Note } from '../../note';

const inputTw =
'focus-visible:ring-2 focus-visible:ring-red-500 focus-visible:outline-0 block w-full appearance-none rounded-lg px-3 py-2 transition-colors text-base leading-tight md:text-sm bg-black/[.05] dark:bg-zinc-50/10 focus:bg-white dark:focus:bg-dark placeholder:text-zinc-500 dark:placeholder:text-zinc-400 contrast-more:border contrast-more:border-current';
const btnTw =
'focus-visible:ring-2 focus-visible:ring-zinc-50 focus-visible:outline-0 flex items-center justify-center rounded-lg px-2 py-1.5 text-sm font-bold tracking-tight shadow-xl shadow-red-500/20 bg-[#DD0031] hover:bg-opacity-70 text-zinc-800 hover:text-primary-darker';

@Component({
selector: 'trpc-app-home',
standalone: true,
imports: [AsyncPipe, FormsModule, NgFor, DatePipe, NgIf, JsonPipe],
host: {
class: 'block h-full p-4',
},
template: `
<div class="justify-center flex mt-20 mb-8 items-center">
<h1 class="italic text-6xl text-[#DD0031] font-bold">Analog + tRPC</h1>
<img
class="ml-2 block w-32"
alt="Spartan Logo"
src="/assets/spartan.svg"
/>
</div>
<form class="py-2 flex items-center" #f="ngForm" (ngSubmit)="addPost(f)">
<input
required
autocomplete="off"
class="${inputTw}"
name="newTitle"
[(ngModel)]="newTitle"
/>
<button class="ml-2 ${btnTw}">+</button>
</form>
<div class="mt-4">
<div
class="mb-4 p-4 font-normal border border-zinc-500/40 rounded-md"
*ngFor="let note of notes ?? []; trackBy: noteTrackBy"
>
<div class="flex items-center justify-between">
<p class="text-sm text-zinc-400">{{ note.createdAt | date }}</p>
<button
class="!text-xs h-6 !bg-opacity-10 hover:!bg-opacity-50 !text-zinc-50 ${btnTw}"
(click)="removePost(note.id)"
>
x
</button>
</div>
<p class="mb-4">{{ note.note }}</p>
</div>
<div
class="text-center rounded-xl p-20 bg-zinc-950/40"
*ngIf="!loadingPosts && notes.length === 0"
>
<h3 class="text-xl font-medium">No notes yet!</h3>
<p class="text-zinc-400">Add a new one and see them appear here...</p>
</div>
</div>
`,
})
export default class HomeComponent {
private _trpc = injectTRPCClient();
public loadingPosts = false;
public notes: Note[] = [];
public newTitle = '';

constructor() {
waitFor(this._trpc.note.list.query().then((notes) => (this.notes = notes)));
}

public noteTrackBy = (index: number, note: Note) => {
return note.id;
};

public addPost(form: NgForm) {
if (!form.valid) {
form.form.markAllAsTouched();
return;
}
this._trpc.note.create
.mutate({ title: this.newTitle })
.then(() => this.fetchPosts());
this.newTitle = '';
form.form.reset();
}

public removePost(id: number) {
this._trpc.note.remove.mutate({ id }).then(() => this.fetchPosts());
}

private fetchPosts() {
this.loadingPosts = true;
this._trpc.note.list.query().then((notes) => {
this.loadingPosts = false;
this.notes = notes;
});
}
}
20 changes: 20 additions & 0 deletions apps/trpc-app/src/main.server.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import 'zone.js/node';
import { enableProdMode } from '@angular/core';
import { renderApplication } from '@angular/platform-server';
import { AppComponent } from './app/app.component';
import { bootstrapApplication } from '@angular/platform-browser';
import { config } from './app.config.server';

if (import.meta.env.PROD) {
enableProdMode();
}

const bootstrap = () => bootstrapApplication(AppComponent, config);

export default async function render(url: string, document: string) {
const html = await renderApplication(bootstrap, {
document,
url,
});
return html;
}
9 changes: 9 additions & 0 deletions apps/trpc-app/src/main.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import 'zone.js';
import { bootstrapApplication } from '@angular/platform-browser';

import { AppComponent } from './app/app.component';
import { appConfig } from './app.config';

bootstrapApplication(AppComponent, appConfig).catch((err) =>
console.error(err)
);
5 changes: 5 additions & 0 deletions apps/trpc-app/src/note.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export type Note = {
id: number;
note: string;
createdAt: Date;
};
Empty file.
7 changes: 7 additions & 0 deletions apps/trpc-app/src/public/assets/spartan.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
8 changes: 8 additions & 0 deletions apps/trpc-app/src/server/routes/trpc/[trpc].ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { appRouter } from '../../trpc/routers';
import { createContext } from '../../trpc/context';
import { createTrpcNitroHandler } from '@analogjs/trpc';
// export API handler
export default createTrpcNitroHandler({
router: appRouter,
createContext,
});
7 changes: 7 additions & 0 deletions apps/trpc-app/src/server/trpc/context.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { inferAsyncReturnType } from '@trpc/server';
/**
* Creates context for an incoming request
* @link https://trpc.io/docs/context
*/
export const createContext = () => ({});
export type Context = inferAsyncReturnType<typeof createContext>;
8 changes: 8 additions & 0 deletions apps/trpc-app/src/server/trpc/routers/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { router } from '../trpc';
import { noteRouter } from './notes';

export const appRouter = router({
note: noteRouter,
});
// export type definition of API
export type AppRouter = typeof appRouter;
Loading

0 comments on commit 9b3382c

Please sign in to comment.