backend-patterns

安装量: 2.5K
排名: #799

安装

npx skills add https://github.com/affaan-m/everything-claude-code --skill backend-patterns
Backend Development Patterns
Backend architecture patterns and best practices for scalable server-side applications.
When to Activate
Designing REST or GraphQL API endpoints
Implementing repository, service, or controller layers
Optimizing database queries (N+1, indexing, connection pooling)
Adding caching (Redis, in-memory, HTTP cache headers)
Setting up background jobs or async processing
Structuring error handling and validation for APIs
Building middleware (auth, logging, rate limiting)
API Design Patterns
RESTful API Structure
// ✅ Resource-based URLs
GET
/
api
/
markets # List resources
GET
/
api
/
markets
/
:
id # Get single resource
POST
/
api
/
markets # Create resource
PUT
/
api
/
markets
/
:
id # Replace resource
PATCH
/
api
/
markets
/
:
id # Update resource
DELETE
/
api
/
markets
/
:
id # Delete resource
// ✅ Query parameters for filtering, sorting, pagination
GET
/
api
/
markets
?
status
=
active
&
sort
=
volume
&
limit
=
20
&
offset
=
0
Repository Pattern
// Abstract data access logic
interface
MarketRepository
{
findAll
(
filters
?
:
MarketFilters
)
:
Promise
<
Market
[
]
>
findById
(
id
:
string
)
:
Promise
<
Market
|
null
>
create
(
data
:
CreateMarketDto
)
:
Promise
<
Market
>
update
(
id
:
string
,
data
:
UpdateMarketDto
)
:
Promise
<
Market
>
delete
(
id
:
string
)
:
Promise
<
void
>
}
class
SupabaseMarketRepository
implements
MarketRepository
{
async
findAll
(
filters
?
:
MarketFilters
)
:
Promise
<
Market
[
]
>
{
let
query
=
supabase
.
from
(
'markets'
)
.
select
(
'*'
)
if
(
filters
?.
status
)
{
query
=
query
.
eq
(
'status'
,
filters
.
status
)
}
if
(
filters
?.
limit
)
{
query
=
query
.
limit
(
filters
.
limit
)
}
const
{
data
,
error
}
=
await
query
if
(
error
)
throw
new
Error
(
error
.
message
)
return
data
}
// Other methods...
}
Service Layer Pattern
// Business logic separated from data access
class
MarketService
{
constructor
(
private
marketRepo
:
MarketRepository
)
{
}
async
searchMarkets
(
query
:
string
,
limit
:
number
=
10
)
:
Promise
<
Market
[
]
>
{
// Business logic
const
embedding
=
await
generateEmbedding
(
query
)
const
results
=
await
this
.
vectorSearch
(
embedding
,
limit
)
// Fetch full data
const
markets
=
await
this
.
marketRepo
.
findByIds
(
results
.
map
(
r
=>
r
.
id
)
)
// Sort by similarity
return
markets
.
sort
(
(
a
,
b
)
=>
{
const
scoreA
=
results
.
find
(
r
=>
r
.
id
===
a
.
id
)
?.
score
||
0
const
scoreB
=
results
.
find
(
r
=>
r
.
id
===
b
.
id
)
?.
score
||
0
return
scoreA
-
scoreB
}
)
}
private
async
vectorSearch
(
embedding
:
number
[
]
,
limit
:
number
)
{
// Vector search implementation
}
}
Middleware Pattern
// Request/response processing pipeline
export
function
withAuth
(
handler
:
NextApiHandler
)
:
NextApiHandler
{
return
async
(
req
,
res
)
=>
{
const
token
=
req
.
headers
.
authorization
?.
replace
(
'Bearer '
,
''
)
if
(
!
token
)
{
return
res
.
status
(
401
)
.
json
(
{
error
:
'Unauthorized'
}
)
}
try
{
const
user
=
await
verifyToken
(
token
)
req
.
user
=
user
return
handler
(
req
,
res
)
}
catch
(
error
)
{
return
res
.
status
(
401
)
.
json
(
{
error
:
'Invalid token'
}
)
}
}
}
// Usage
export
default
withAuth
(
async
(
req
,
res
)
=>
{
// Handler has access to req.user
}
)
Database Patterns
Query Optimization
// ✅ GOOD: Select only needed columns
const
{
data
}
=
await
supabase
.
from
(
'markets'
)
.
select
(
'id, name, status, volume'
)
.
eq
(
'status'
,
'active'
)
.
order
(
'volume'
,
{
ascending
:
false
}
)
.
limit
(
10
)
// ❌ BAD: Select everything
const
{
data
}
=
await
supabase
.
from
(
'markets'
)
.
select
(
'*'
)
N+1 Query Prevention
// ❌ BAD: N+1 query problem
const
markets
=
await
getMarkets
(
)
for
(
const
market
of
markets
)
{
market
.
creator
=
await
getUser
(
market
.
creator_id
)
// N queries
}
// ✅ GOOD: Batch fetch
const
markets
=
await
getMarkets
(
)
const
creatorIds
=
markets
.
map
(
m
=>
m
.
creator_id
)
const
creators
=
await
getUsers
(
creatorIds
)
// 1 query
const
creatorMap
=
new
Map
(
creators
.
map
(
c
=>
[
c
.
id
,
c
]
)
)
markets
.
forEach
(
market
=>
{
market
.
creator
=
creatorMap
.
get
(
market
.
creator_id
)
}
)
Transaction Pattern
async
function
createMarketWithPosition
(
marketData
:
CreateMarketDto
,
positionData
:
CreatePositionDto
)
{
// Use Supabase transaction
const
{
data
,
error
}
=
await
supabase
.
rpc
(
'create_market_with_position'
,
{
market_data
:
marketData
,
position_data
:
positionData
}
)
if
(
error
)
throw
new
Error
(
'Transaction failed'
)
return
data
}
// SQL function in Supabase
CREATE
OR
REPLACE
FUNCTION
create_market_with_position
(
market_data jsonb
,
position_data jsonb
)
RETURNS
jsonb
LANGUAGE
plpgsql
AS
$$
BEGIN
--
Start transaction automatically
INSERT
INTO
markets
VALUES
(
market_data
)
;
INSERT
INTO
positions
VALUES
(
position_data
)
;
RETURN
jsonb_build_object
(
'success'
,
true
)
;
EXCEPTION
WHEN
OTHERS
THEN
--
Rollback happens automatically
RETURN
jsonb_build_object
(
'success'
,
false
,
'error'
,
SQLERRM
)
;
END
;
$$
;
Caching Strategies
Redis Caching Layer
class
CachedMarketRepository
implements
MarketRepository
{
constructor
(
private
baseRepo
:
MarketRepository
,
private
redis
:
RedisClient
)
{
}
async
findById
(
id
:
string
)
:
Promise
<
Market
|
null
>
{
// Check cache first
const
cached
=
await
this
.
redis
.
get
(
`
market:
${
id
}
`
)
if
(
cached
)
{
return
JSON
.
parse
(
cached
)
}
// Cache miss - fetch from database
const
market
=
await
this
.
baseRepo
.
findById
(
id
)
if
(
market
)
{
// Cache for 5 minutes
await
this
.
redis
.
setex
(
`
market:
${
id
}
`
,
300
,
JSON
.
stringify
(
market
)
)
}
return
market
}
async
invalidateCache
(
id
:
string
)
:
Promise
<
void
>
{
await
this
.
redis
.
del
(
`
market:
${
id
}
`
)
}
}
Cache-Aside Pattern
async
function
getMarketWithCache
(
id
:
string
)
:
Promise
<
Market
>
{
const
cacheKey
=
`
market:
${
id
}
`
// Try cache
const
cached
=
await
redis
.
get
(
cacheKey
)
if
(
cached
)
return
JSON
.
parse
(
cached
)
// Cache miss - fetch from DB
const
market
=
await
db
.
markets
.
findUnique
(
{
where
:
{
id
}
}
)
if
(
!
market
)
throw
new
Error
(
'Market not found'
)
// Update cache
await
redis
.
setex
(
cacheKey
,
300
,
JSON
.
stringify
(
market
)
)
return
market
}
Error Handling Patterns
Centralized Error Handler
class
ApiError
extends
Error
{
constructor
(
public
statusCode
:
number
,
public
message
:
string
,
public
isOperational
=
true
)
{
super
(
message
)
Object
.
setPrototypeOf
(
this
,
ApiError
.
prototype
)
}
}
export
function
errorHandler
(
error
:
unknown
,
req
:
Request
)
:
Response
{
if
(
error
instanceof
ApiError
)
{
return
NextResponse
.
json
(
{
success
:
false
,
error
:
error
.
message
}
,
{
status
:
error
.
statusCode
}
)
}
if
(
error
instanceof
z
.
ZodError
)
{
return
NextResponse
.
json
(
{
success
:
false
,
error
:
'Validation failed'
,
details
:
error
.
errors
}
,
{
status
:
400
}
)
}
// Log unexpected errors
console
.
error
(
'Unexpected error:'
,
error
)
return
NextResponse
.
json
(
{
success
:
false
,
error
:
'Internal server error'
}
,
{
status
:
500
}
)
}
// Usage
export
async
function
GET
(
request
:
Request
)
{
try
{
const
data
=
await
fetchData
(
)
return
NextResponse
.
json
(
{
success
:
true
,
data
}
)
}
catch
(
error
)
{
return
errorHandler
(
error
,
request
)
}
}
Retry with Exponential Backoff
async
function
fetchWithRetry
<
T
>
(
fn
:
(
)
=>
Promise
<
T
>
,
maxRetries
=
3
)
:
Promise
<
T
>
{
let
lastError
:
Error
for
(
let
i
=
0
;
i
<
maxRetries
;
i
++
)
{
try
{
return
await
fn
(
)
}
catch
(
error
)
{
lastError
=
error
as
Error
if
(
i
<
maxRetries
-
1
)
{
// Exponential backoff: 1s, 2s, 4s
const
delay
=
Math
.
pow
(
2
,
i
)
*
1000
await
new
Promise
(
resolve
=>
setTimeout
(
resolve
,
delay
)
)
}
}
}
throw
lastError
!
}
// Usage
const
data
=
await
fetchWithRetry
(
(
)
=>
fetchFromAPI
(
)
)
Authentication & Authorization
JWT Token Validation
import
jwt
from
'jsonwebtoken'
interface
JWTPayload
{
userId
:
string
email
:
string
role
:
'admin'
|
'user'
}
export
function
verifyToken
(
token
:
string
)
:
JWTPayload
{
try
{
const
payload
=
jwt
.
verify
(
token
,
process
.
env
.
JWT_SECRET
!
)
as
JWTPayload
return
payload
}
catch
(
error
)
{
throw
new
ApiError
(
401
,
'Invalid token'
)
}
}
export
async
function
requireAuth
(
request
:
Request
)
{
const
token
=
request
.
headers
.
get
(
'authorization'
)
?.
replace
(
'Bearer '
,
''
)
if
(
!
token
)
{
throw
new
ApiError
(
401
,
'Missing authorization token'
)
}
return
verifyToken
(
token
)
}
// Usage in API route
export
async
function
GET
(
request
:
Request
)
{
const
user
=
await
requireAuth
(
request
)
const
data
=
await
getDataForUser
(
user
.
userId
)
return
NextResponse
.
json
(
{
success
:
true
,
data
}
)
}
Role-Based Access Control
type
Permission
=
'read'
|
'write'
|
'delete'
|
'admin'
interface
User
{
id
:
string
role
:
'admin'
|
'moderator'
|
'user'
}
const
rolePermissions
:
Record
<
User
[
'role'
]
,
Permission
[
]
>
=
{
admin
:
[
'read'
,
'write'
,
'delete'
,
'admin'
]
,
moderator
:
[
'read'
,
'write'
,
'delete'
]
,
user
:
[
'read'
,
'write'
]
}
export
function
hasPermission
(
user
:
User
,
permission
:
Permission
)
:
boolean
{
return
rolePermissions
[
user
.
role
]
.
includes
(
permission
)
}
export
function
requirePermission
(
permission
:
Permission
)
{
return
(
handler
:
(
request
:
Request
,
user
:
User
)
=>
Promise
<
Response
>
)
=>
{
return
async
(
request
:
Request
)
=>
{
const
user
=
await
requireAuth
(
request
)
if
(
!
hasPermission
(
user
,
permission
)
)
{
throw
new
ApiError
(
403
,
'Insufficient permissions'
)
}
return
handler
(
request
,
user
)
}
}
}
// Usage - HOF wraps the handler
export
const
DELETE
=
requirePermission
(
'delete'
)
(
async
(
request
:
Request
,
user
:
User
)
=>
{
// Handler receives authenticated user with verified permission
return
new
Response
(
'Deleted'
,
{
status
:
200
}
)
}
)
Rate Limiting
Simple In-Memory Rate Limiter
class
RateLimiter
{
private
requests
=
new
Map
<
string
,
number
[
]
>
(
)
async
checkLimit
(
identifier
:
string
,
maxRequests
:
number
,
windowMs
:
number
)
:
Promise
<
boolean
>
{
const
now
=
Date
.
now
(
)
const
requests
=
this
.
requests
.
get
(
identifier
)
||
[
]
// Remove old requests outside window
const
recentRequests
=
requests
.
filter
(
time
=>
now
-
time
<
windowMs
)
if
(
recentRequests
.
length
>=
maxRequests
)
{
return
false
// Rate limit exceeded
}
// Add current request
recentRequests
.
push
(
now
)
this
.
requests
.
set
(
identifier
,
recentRequests
)
return
true
}
}
const
limiter
=
new
RateLimiter
(
)
export
async
function
GET
(
request
:
Request
)
{
const
ip
=
request
.
headers
.
get
(
'x-forwarded-for'
)
||
'unknown'
const
allowed
=
await
limiter
.
checkLimit
(
ip
,
100
,
60000
)
// 100 req/min
if
(
!
allowed
)
{
return
NextResponse
.
json
(
{
error
:
'Rate limit exceeded'
}
,
{
status
:
429
}
)
}
// Continue with request
}
Background Jobs & Queues
Simple Queue Pattern
class
JobQueue
<
T
>
{
private
queue
:
T
[
]
=
[
]
private
processing
=
false
async
add
(
job
:
T
)
:
Promise
<
void
>
{
this
.
queue
.
push
(
job
)
if
(
!
this
.
processing
)
{
this
.
process
(
)
}
}
private
async
process
(
)
:
Promise
<
void
>
{
this
.
processing
=
true
while
(
this
.
queue
.
length
>
0
)
{
const
job
=
this
.
queue
.
shift
(
)
!
try
{
await
this
.
execute
(
job
)
}
catch
(
error
)
{
console
.
error
(
'Job failed:'
,
error
)
}
}
this
.
processing
=
false
}
private
async
execute
(
job
:
T
)
:
Promise
<
void
>
{
// Job execution logic
}
}
// Usage for indexing markets
interface
IndexJob
{
marketId
:
string
}
const
indexQueue
=
new
JobQueue
<
IndexJob
>
(
)
export
async
function
POST
(
request
:
Request
)
{
const
{
marketId
}
=
await
request
.
json
(
)
// Add to queue instead of blocking
await
indexQueue
.
add
(
{
marketId
}
)
return
NextResponse
.
json
(
{
success
:
true
,
message
:
'Job queued'
}
)
}
Logging & Monitoring
Structured Logging
interface
LogContext
{
userId
?
:
string
requestId
?
:
string
method
?
:
string
path
?
:
string
[
key
:
string
]
:
unknown
}
class
Logger
{
log
(
level
:
'info'
|
'warn'
|
'error'
,
message
:
string
,
context
?
:
LogContext
)
{
const
entry
=
{
timestamp
:
new
Date
(
)
.
toISOString
(
)
,
level
,
message
,
...
context
}
console
.
log
(
JSON
.
stringify
(
entry
)
)
}
info
(
message
:
string
,
context
?
:
LogContext
)
{
this
.
log
(
'info'
,
message
,
context
)
}
warn
(
message
:
string
,
context
?
:
LogContext
)
{
this
.
log
(
'warn'
,
message
,
context
)
}
error
(
message
:
string
,
error
:
Error
,
context
?
:
LogContext
)
{
this
.
log
(
'error'
,
message
,
{
...
context
,
error
:
error
.
message
,
stack
:
error
.
stack
}
)
}
}
const
logger
=
new
Logger
(
)
// Usage
export
async
function
GET
(
request
:
Request
)
{
const
requestId
=
crypto
.
randomUUID
(
)
logger
.
info
(
'Fetching markets'
,
{
requestId
,
method
:
'GET'
,
path
:
'/api/markets'
}
)
try
{
const
markets
=
await
fetchMarkets
(
)
return
NextResponse
.
json
(
{
success
:
true
,
data
:
markets
}
)
}
catch
(
error
)
{
logger
.
error
(
'Failed to fetch markets'
,
error
as
Error
,
{
requestId
}
)
return
NextResponse
.
json
(
{
error
:
'Internal error'
}
,
{
status
:
500
}
)
}
}
Remember
Backend patterns enable scalable, maintainable server-side applications. Choose patterns that fit your complexity level.
返回排行榜