diff --git a/examples/next-with-approuter/src/app/page.tsx b/examples/next-with-approuter/src/app/page.tsx index 86d95df..40e0059 100644 --- a/examples/next-with-approuter/src/app/page.tsx +++ b/examples/next-with-approuter/src/app/page.tsx @@ -5,7 +5,7 @@ export default async function Home() { await trackEvent('page_view', { page: 'home' }); return (
- Hello from Next.js App Router
+ Aptabase + Next.js App Router
); } diff --git a/examples/next-with-pages/next-env.d.ts b/examples/next-with-pages/next-env.d.ts new file mode 100644 index 0000000..4f11a03 --- /dev/null +++ b/examples/next-with-pages/next-env.d.ts @@ -0,0 +1,5 @@ +/// +/// + +// NOTE: This file should not be edited +// see https://nextjs.org/docs/basic-features/typescript for more information. diff --git a/examples/next-with-pages/next.config.js b/examples/next-with-pages/next.config.js new file mode 100644 index 0000000..e904157 --- /dev/null +++ b/examples/next-with-pages/next.config.js @@ -0,0 +1,9 @@ +/** @type {import('next').NextConfig} */ +const nextConfig = { + reactStrictMode: true, + experimental: { + instrumentationHook: true, + }, +}; + +module.exports = nextConfig; diff --git a/examples/next-with-pages/package.json b/examples/next-with-pages/package.json new file mode 100644 index 0000000..3e44817 --- /dev/null +++ b/examples/next-with-pages/package.json @@ -0,0 +1,21 @@ +{ + "name": "next-with-pages", + "version": "0.1.0", + "private": true, + "scripts": { + "dev": "next dev -p 4000", + "build": "next build", + "start": "next start", + "lint": "next lint" + }, + "dependencies": { + "@types/node": "20.5.7", + "@types/react": "18.2.21", + "@types/react-dom": "18.2.7", + "next": "13.4.19", + "react": "18.2.0", + "react-dom": "18.2.0", + "typescript": "5.2.2", + "@aptabase/nextjs": "*" + } +} diff --git a/examples/next-with-pages/src/components/Counter.tsx b/examples/next-with-pages/src/components/Counter.tsx new file mode 100644 index 0000000..6e11999 --- /dev/null +++ b/examples/next-with-pages/src/components/Counter.tsx @@ -0,0 +1,25 @@ +import { useAptabase } from '@aptabase/nextjs'; +import { useState } from 'react'; + +export function Counter() { + const { trackEvent } = useAptabase(); + const [count, setCount] = useState(0); + + function increment() { + setCount((c) => c + 1); + trackEvent('increment'); + } + + function decrement() { + setCount((c) => c - 1); + trackEvent('decrement'); + } + + return ( +
+

Count: {count}

+ + +
+ ); +} diff --git a/examples/next-with-pages/src/instrumentation.ts b/examples/next-with-pages/src/instrumentation.ts new file mode 100644 index 0000000..9de5fce --- /dev/null +++ b/examples/next-with-pages/src/instrumentation.ts @@ -0,0 +1,5 @@ +import { init } from '@aptabase/nextjs'; + +export function register() { + init('A-DEV-0000000000'); +} diff --git a/examples/next-with-pages/src/pages/_app.tsx b/examples/next-with-pages/src/pages/_app.tsx new file mode 100644 index 0000000..1372cda --- /dev/null +++ b/examples/next-with-pages/src/pages/_app.tsx @@ -0,0 +1,10 @@ +import { AptabaseProvider } from '@aptabase/nextjs'; +import type { AppProps } from 'next/app'; + +export default function App({ Component, pageProps }: AppProps) { + return ( + + + + ); +} diff --git a/examples/next-with-pages/src/pages/_document.tsx b/examples/next-with-pages/src/pages/_document.tsx new file mode 100644 index 0000000..54e8bf3 --- /dev/null +++ b/examples/next-with-pages/src/pages/_document.tsx @@ -0,0 +1,13 @@ +import { Html, Head, Main, NextScript } from 'next/document' + +export default function Document() { + return ( + + + +
+ + + + ) +} diff --git a/examples/next-with-pages/src/pages/api/hello.ts b/examples/next-with-pages/src/pages/api/hello.ts new file mode 100644 index 0000000..1d31a5c --- /dev/null +++ b/examples/next-with-pages/src/pages/api/hello.ts @@ -0,0 +1,12 @@ +// Next.js API route support: https://nextjs.org/docs/api-routes/introduction +import { trackEvent } from '@aptabase/nextjs/server'; +import type { NextApiRequest, NextApiResponse } from 'next'; + +type Data = { + name: string; +}; + +export default async function handler(req: NextApiRequest, res: NextApiResponse) { + await trackEvent('hello', { name: 'John Doe' }, req); + res.status(200).json({ name: 'John Doe' }); +} diff --git a/examples/next-with-pages/src/pages/index.tsx b/examples/next-with-pages/src/pages/index.tsx new file mode 100644 index 0000000..f568c3c --- /dev/null +++ b/examples/next-with-pages/src/pages/index.tsx @@ -0,0 +1,25 @@ +import { Counter } from '@/components/Counter'; +import { trackEvent } from '@aptabase/nextjs/server'; +import { GetServerSideProps } from 'next'; +import Head from 'next/head'; + +export const getServerSideProps: GetServerSideProps = async ({ req }) => { + await trackEvent('page_view', { page: 'home' }, req); + return { props: {} }; +}; + +export default function Home() { + return ( + <> + + Create Next App + + + +
+ Aptabase + Next.js Pages Router
+ +
+ + ); +} diff --git a/examples/next-with-pages/tsconfig.json b/examples/next-with-pages/tsconfig.json new file mode 100644 index 0000000..3ca6a9a --- /dev/null +++ b/examples/next-with-pages/tsconfig.json @@ -0,0 +1,22 @@ +{ + "compilerOptions": { + "target": "es5", + "lib": ["dom", "dom.iterable", "esnext"], + "allowJs": true, + "skipLibCheck": true, + "strict": true, + "noEmit": true, + "esModuleInterop": true, + "module": "esnext", + "moduleResolution": "bundler", + "resolveJsonModule": true, + "isolatedModules": true, + "jsx": "preserve", + "incremental": true, + "paths": { + "@/*": ["./src/*"] + } + }, + "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"], + "exclude": ["node_modules"] +} diff --git a/package-lock.json b/package-lock.json index c99fbdc..fc078f4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -33,6 +33,19 @@ "typescript": "5.2.2" } }, + "examples/next-with-pages": { + "version": "0.1.0", + "dependencies": { + "@aptabase/nextjs": "*", + "@types/node": "20.5.7", + "@types/react": "18.2.21", + "@types/react-dom": "18.2.7", + "next": "13.4.19", + "react": "18.2.0", + "react-dom": "18.2.0", + "typescript": "5.2.2" + } + }, "node_modules/@aptabase/nextjs": { "resolved": "packages/nextjs", "link": true @@ -590,6 +603,10 @@ "resolved": "examples/next-with-approuter", "link": true }, + "node_modules/next-with-pages": { + "resolved": "examples/next-with-pages", + "link": true + }, "node_modules/path-parse": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", diff --git a/packages/nextjs/src/server.ts b/packages/nextjs/src/server.ts index 1bbb563..aef9837 100644 --- a/packages/nextjs/src/server.ts +++ b/packages/nextjs/src/server.ts @@ -1,9 +1,17 @@ +import { type NextIncomingMessage } from 'next/dist/server/request-meta'; import { headers } from 'next/headers'; -export async function trackEvent(eventName: string, props?: Record): Promise { +export async function trackEvent( + eventName: string, + props?: Record, + req?: NextIncomingMessage, +): Promise { const appKey = globalThis.__APTABASE__.appKey; if (!appKey) return Promise.resolve(); + const userAgent = getUserAgent(req); + if (!userAgent) return Promise.resolve(); + const body = JSON.stringify({ timestamp: new Date().toISOString(), sessionId: 'CHANGE-THIS', @@ -21,7 +29,7 @@ export async function trackEvent(eventName: string, props?: Record